зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1852340 - part 1: Add a new Gecko component for reporting broken websites; r=ayeddi,fluent-reviewers,mossop,flod,Gijs
Differential Revision: https://phabricator.services.mozilla.com/D190647
This commit is contained in:
Родитель
482c524980
Коммит
6cb66d4ccd
|
@ -2900,6 +2900,21 @@ pref("cookiebanners.ui.desktop.cfrVariant", 0);
|
|||
pref("dom.security.credentialmanagement.identity.enabled", true);
|
||||
#endif
|
||||
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
pref("ui.new-webcompat-reporter.enabled", true);
|
||||
#else
|
||||
pref("ui.new-webcompat-reporter.enabled", false);
|
||||
#endif
|
||||
|
||||
#if defined(EARLY_BETA_OR_EARLIER)
|
||||
pref("ui.new-webcompat-reporter.send-more-info-link", true);
|
||||
#else
|
||||
pref("ui.new-webcompat-reporter.send-more-info-link", false);
|
||||
#endif
|
||||
|
||||
# 0 = disabled, 1 = reason optional, 2 = reason required.
|
||||
pref("ui.new-webcompat-reporter.reason-dropdown", 0);
|
||||
|
||||
// Reset Private Browsing Session feature
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
pref("browser.privatebrowsing.resetPBM.enabled", true);
|
||||
|
|
|
@ -146,6 +146,11 @@
|
|||
data-l10n-id="appmenuitem-more-tools"
|
||||
closemenu="none"
|
||||
oncommand="PanelUI.showMoreToolsPanel(this);"/>
|
||||
<toolbarbutton id="appMenu-report-broken-site-button"
|
||||
class="subviewbutton subviewbutton-nav"
|
||||
data-l10n-id="appmenuitem-report-broken-site"
|
||||
closemenu="none"
|
||||
command="cmd_reportBrokenSite"/>
|
||||
<toolbarbutton id="appMenu-help-button2"
|
||||
class="subviewbutton subviewbutton-nav"
|
||||
data-l10n-id="appmenuitem-help"
|
||||
|
|
|
@ -472,6 +472,10 @@
|
|||
#else
|
||||
/>
|
||||
#endif
|
||||
<menuitem id="help_reportBrokenSite"
|
||||
command="cmd_reportBrokenSite"
|
||||
data-l10n-id="menu-report-broken-site"
|
||||
appmenu-data-l10n-id="menu-report-broken-site"/>
|
||||
<menuitem id="feedbackPage"
|
||||
oncommand="openFeedbackPage()"
|
||||
data-l10n-id="menu-help-share-ideas"
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#ifdef XP_MACOSX
|
||||
<command id="cmd_findSelection" oncommand="gLazyFindCommand('onFindSelectionCommand')"/>
|
||||
#endif
|
||||
<command id="cmd_reportBrokenSite" oncommand="ReportBrokenSite.open(event);"/>
|
||||
<command id="cmd_translate" oncommand="TranslationsPanel.open(event);"/>
|
||||
<!-- work-around bug 392512 -->
|
||||
<command id="Browser:AddBookmarkAs"
|
||||
|
|
|
@ -65,6 +65,7 @@ ChromeUtils.defineESModuleGetters(this, {
|
|||
PromptUtils: "resource://gre/modules/PromptUtils.sys.mjs",
|
||||
ReaderMode: "resource://gre/modules/ReaderMode.sys.mjs",
|
||||
ResetPBMPanel: "resource:///modules/ResetPBMPanel.sys.mjs",
|
||||
ReportBrokenSite: "resource:///modules/ReportBrokenSite.sys.mjs",
|
||||
SafeBrowsing: "resource://gre/modules/SafeBrowsing.sys.mjs",
|
||||
Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
|
||||
SaveToPocket: "chrome://pocket/content/SaveToPocket.sys.mjs",
|
||||
|
@ -1879,6 +1880,7 @@ var gBrowserInit = {
|
|||
// apply full zoom settings to tabs restored by the session restore service.
|
||||
FullZoom.init();
|
||||
PanelUI.init(shouldSuppressPopupNotifications);
|
||||
ReportBrokenSite.init(gBrowser);
|
||||
|
||||
UpdateUrlbarSearchSplitterState();
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
<link rel="localization" href="browser/panelUI.ftl"/>
|
||||
<link rel="localization" href="browser/places.ftl"/>
|
||||
<link rel="localization" href="browser/protectionsPanel.ftl"/>
|
||||
<link rel="localization" href="browser/reportBrokenSite.ftl"/>
|
||||
<link rel="localization" href="browser/screenshots.ftl"/>
|
||||
<link rel="localization" href="browser/search.ftl"/>
|
||||
<link rel="localization" href="browser/sidebarMenu.ftl"/>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<html:link rel="localization" href="browser/browserSets.ftl"/>
|
||||
<html:link rel="localization" href="browser/firefoxView.ftl"/>
|
||||
<html:link rel="localization" href="browser/menubar.ftl"/>
|
||||
<html:link rel="localization" href="browser/reportBrokenSite.ftl"/>
|
||||
<html:link rel="localization" href="browser/screenshots.ftl"/>
|
||||
<html:link rel="localization" href="toolkit/branding/accounts.ftl"/>
|
||||
<html:link rel="localization" href="toolkit/branding/brandings.ftl"/>
|
||||
|
|
|
@ -500,6 +500,7 @@
|
|||
#include ../../components/controlcenter/content/protectionsPanel.inc.xhtml
|
||||
#include ../../components/downloads/content/downloadsPanel.inc.xhtml
|
||||
#include ../../components/translations/content/translationsPanel.inc.xhtml
|
||||
#include ../../components/reportbrokensite/content/reportBrokenSitePanel.inc.xhtml
|
||||
#include browser-allTabsMenu.inc.xhtml
|
||||
|
||||
<tooltip id="dynamic-shortcut-tooltip"
|
||||
|
|
|
@ -286,9 +286,6 @@ var allowlist = [
|
|||
{ file: "chrome://browser/content/screenshots/copy.svg" },
|
||||
{ file: "chrome://browser/content/screenshots/download.svg" },
|
||||
{ file: "chrome://browser/content/screenshots/download-white.svg" },
|
||||
|
||||
// Bug 1852340: adding strings early for new broken website reporting component
|
||||
{ file: "resource://app/localization/en-US/browser/reportBrokenSite.ftl" },
|
||||
];
|
||||
|
||||
if (AppConstants.NIGHTLY_BUILD && AppConstants.platform != "win") {
|
||||
|
|
|
@ -20,6 +20,7 @@ ChromeUtils.defineESModuleGetters(this, {
|
|||
ExtensionSettingsStore:
|
||||
"resource://gre/modules/ExtensionSettingsStore.sys.mjs",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
|
||||
ReportBrokenSite: "resource:///modules/ReportBrokenSite.sys.mjs",
|
||||
ShellService: "resource:///modules/ShellService.sys.mjs",
|
||||
URILoadingHelper: "resource:///modules/URILoadingHelper.sys.mjs",
|
||||
});
|
||||
|
@ -540,30 +541,6 @@ function buildHelpMenu() {
|
|||
gSafeBrowsing.setReportPhishingMenu();
|
||||
}
|
||||
|
||||
// We're testing to see if the WebCompat team's "Report Site Issue"
|
||||
// access point makes sense in the Help menu. Normally checking this
|
||||
// pref wouldn't be enough, since there's also the case that the
|
||||
// add-on has somehow been disabled by the user or third-party software
|
||||
// without flipping the pref. Since this add-on is only used on pre-release
|
||||
// channels, and since the jury is still out on whether or not the Help menu
|
||||
// is the right place for this item, we're going to do a least-effort
|
||||
// approach here and assume that the pref is enough to determine whether the
|
||||
// menuitem should appear.
|
||||
//
|
||||
// See bug 1690573 for further details.
|
||||
let reportSiteIssueEnabled = Services.prefs.getBoolPref(
|
||||
"extensions.webcompat-reporter.enabled",
|
||||
false
|
||||
);
|
||||
let reportSiteIssue = document.getElementById("help_reportSiteIssue");
|
||||
reportSiteIssue.hidden = !reportSiteIssueEnabled;
|
||||
if (reportSiteIssueEnabled) {
|
||||
let uri = gBrowser.currentURI;
|
||||
let isReportablePage =
|
||||
uri && (uri.schemeIs("http") || uri.schemeIs("https"));
|
||||
reportSiteIssue.disabled = !isReportablePage;
|
||||
}
|
||||
|
||||
if (NimbusFeatures.deviceMigration.getVariable("helpMenuHidden")) {
|
||||
let helpMenuItem = document.getElementById("helpSwitchDevice");
|
||||
helpMenuItem.hidden = true;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
role="alertdialog"
|
||||
noautofocus="true"
|
||||
aria-labelledby="protections-popup-main-header-label"
|
||||
onpopupshown="gProtectionsHandler.onPopupShown(event);"
|
||||
onpopupshown="gProtectionsHandler.onPopupShown(event);ReportBrokenSite.updateParentMenu(event)"
|
||||
onpopuphidden="gProtectionsHandler.onPopupHidden(event);"
|
||||
orient="vertical">
|
||||
|
||||
|
@ -63,6 +63,13 @@
|
|||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<toolbarseparator observes="cmd_reportBrokenSite"></toolbarseparator>
|
||||
<toolbarbutton id="protections-popup-report-broken-site-button"
|
||||
command="cmd_reportBrokenSite"
|
||||
class="subviewbutton subviewbutton-nav"
|
||||
data-l10n-id="protections-panel-report-broken-site"
|
||||
closemenu="none"/>
|
||||
|
||||
<!-- Tracking Protection Section -->
|
||||
<toolbarseparator></toolbarseparator>
|
||||
<vbox id="tracking-protection-container" class="protections-popup-section">
|
||||
|
|
|
@ -51,6 +51,7 @@ DIRS += [
|
|||
"prompts",
|
||||
"protections",
|
||||
"protocolhandler",
|
||||
"reportbrokensite",
|
||||
"resistfingerprinting",
|
||||
"screenshots",
|
||||
"search",
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
/* 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/. */
|
||||
|
||||
/* eslint-env mozilla/browser-window */
|
||||
|
||||
const DEFAULT_NEW_REPORT_ENDPOINT = "https://webcompat.com/issues/new";
|
||||
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
const gDescriptionCheckRE = /\S/;
|
||||
|
||||
class ViewState {
|
||||
#doc;
|
||||
#mainView;
|
||||
#reportSentView;
|
||||
|
||||
currentTabURI;
|
||||
currentTabWebcompatDetailsPromise;
|
||||
isURLValid = false;
|
||||
isDescriptionValid = false;
|
||||
isReasonValid = false;
|
||||
|
||||
constructor(doc) {
|
||||
this.#doc = doc;
|
||||
this.#mainView = doc.ownerGlobal.PanelMultiView.getViewNode(
|
||||
this.#doc,
|
||||
"report-broken-site-popup-mainView"
|
||||
);
|
||||
this.#reportSentView = doc.ownerGlobal.PanelMultiView.getViewNode(
|
||||
this.#doc,
|
||||
"report-broken-site-popup-reportSentView"
|
||||
);
|
||||
ViewState.#cache.set(doc, this);
|
||||
}
|
||||
|
||||
static #cache = new WeakMap();
|
||||
static get(doc) {
|
||||
return ViewState.#cache.get(doc) ?? new ViewState(doc);
|
||||
}
|
||||
|
||||
get urlInput() {
|
||||
return this.#mainView.querySelector("#report-broken-site-popup-url");
|
||||
}
|
||||
|
||||
get url() {
|
||||
return this.urlInput.value;
|
||||
}
|
||||
|
||||
set url(spec) {
|
||||
this.urlInput.value = spec;
|
||||
}
|
||||
|
||||
resetURLToCurrentTab() {
|
||||
const { currentURI } = this.#doc.ownerGlobal.gBrowser.selectedBrowser;
|
||||
this.currentTabURI = currentURI;
|
||||
this.urlInput.value = currentURI.spec;
|
||||
this.isURLValid = true;
|
||||
}
|
||||
|
||||
get descriptionInput() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-description"
|
||||
);
|
||||
}
|
||||
|
||||
get description() {
|
||||
return this.descriptionInput.value;
|
||||
}
|
||||
|
||||
set description(value) {
|
||||
this.descriptionInput.value = value;
|
||||
}
|
||||
|
||||
static REASON_CHOICES_ID_PREFIX = "report-broken-site-popup-reason-";
|
||||
|
||||
get reasonInput() {
|
||||
return this.#mainView.querySelector("#report-broken-site-popup-reason");
|
||||
}
|
||||
|
||||
get reason() {
|
||||
const reason = this.reasonInput.selectedItem.id.replace(
|
||||
ViewState.REASON_CHOICES_ID_PREFIX,
|
||||
""
|
||||
);
|
||||
return reason == "choose" ? undefined : reason;
|
||||
}
|
||||
|
||||
set reason(value) {
|
||||
this.reasonInput.selectedItem = this.#mainView.querySelector(
|
||||
`#${ViewState.REASON_CHOICES_ID_PREFIX}${value}`
|
||||
);
|
||||
}
|
||||
|
||||
static CHOOSE_A_REASON_OPT_ID = "report-broken-site-popup-reason-choose";
|
||||
|
||||
get chooseAReasonOption() {
|
||||
return this.#mainView.querySelector(`#${ViewState.CHOOSE_A_REASON_OPT_ID}`);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.currentTabWebcompatDetailsPromise = undefined;
|
||||
|
||||
this.resetURLToCurrentTab();
|
||||
|
||||
this.description = "";
|
||||
this.isDescriptionValid = false;
|
||||
|
||||
this.reason = "choose";
|
||||
this.isReasonValid = false;
|
||||
}
|
||||
|
||||
enableOrDisableSendButton(
|
||||
isReasonEnabled,
|
||||
isReasonOptional,
|
||||
isDescriptionOptional
|
||||
) {
|
||||
const isReasonOkay =
|
||||
!isReasonEnabled || isReasonOptional || this.isReasonValid;
|
||||
|
||||
const isDescriptionOkay = isDescriptionOptional || this.isDescriptionValid;
|
||||
|
||||
this.sendButton.disabled =
|
||||
!this.isURLValid || !isReasonOkay || !isDescriptionOkay;
|
||||
}
|
||||
|
||||
get sendMoreInfoLink() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-send-more-info-link"
|
||||
);
|
||||
}
|
||||
|
||||
get reasonLabelRequired() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-reason-label"
|
||||
);
|
||||
}
|
||||
|
||||
get reasonLabelOptional() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-reason-optional-label"
|
||||
);
|
||||
}
|
||||
|
||||
get descriptionLabelRequired() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-description-label"
|
||||
);
|
||||
}
|
||||
|
||||
get descriptionLabelOptional() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-description-optional-label"
|
||||
);
|
||||
}
|
||||
|
||||
get sendButton() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-send-button"
|
||||
);
|
||||
}
|
||||
|
||||
get cancelButton() {
|
||||
return this.#mainView.querySelector(
|
||||
"#report-broken-site-popup-cancel-button"
|
||||
);
|
||||
}
|
||||
|
||||
get mainView() {
|
||||
return this.#mainView;
|
||||
}
|
||||
|
||||
get reportSentView() {
|
||||
return this.#reportSentView;
|
||||
}
|
||||
|
||||
get okayButton() {
|
||||
return this.#reportSentView.querySelector(
|
||||
"#report-broken-site-popup-okay-button"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export var ReportBrokenSite = new (class ReportBrokenSite {
|
||||
#newReportEndpoint = undefined;
|
||||
|
||||
get sendMoreInfoEndpoint() {
|
||||
return this.#newReportEndpoint || DEFAULT_NEW_REPORT_ENDPOINT;
|
||||
}
|
||||
|
||||
static WEBCOMPAT_REPORTER_CONFIG = {
|
||||
src: "desktop-reporter",
|
||||
utm_campaign: "report-broken-site",
|
||||
utm_source: "desktop-reporter",
|
||||
};
|
||||
|
||||
static DATAREPORTING_PREF = "datareporting.healthreport.uploadEnabled";
|
||||
static REPORTER_ENABLED_PREF = "ui.new-webcompat-reporter.enabled";
|
||||
|
||||
static REASON_PREF = "ui.new-webcompat-reporter.reason-dropdown";
|
||||
static SEND_MORE_INFO_PREF = "ui.new-webcompat-reporter.send-more-info-link";
|
||||
static NEW_REPORT_ENDPOINT_PREF =
|
||||
"ui.new-webcompat-reporter.new-report-endpoint";
|
||||
static REPORT_SITE_ISSUE_PREF = "extensions.webcompat-reporter.enabled";
|
||||
|
||||
static MAIN_PANELVIEW_ID = "report-broken-site-popup-mainView";
|
||||
static SENT_PANELVIEW_ID = "report-broken-site-popup-reportSentView";
|
||||
|
||||
#_enabled = false;
|
||||
get enabled() {
|
||||
return this.#_enabled;
|
||||
}
|
||||
|
||||
#reasonEnabled = false;
|
||||
#reasonIsOptional = true;
|
||||
#descriptionIsOptional = true;
|
||||
#sendMoreInfoEnabled = true;
|
||||
|
||||
constructor() {
|
||||
for (const [name, [pref, dflt]] of Object.entries({
|
||||
dataReportingPref: [ReportBrokenSite.DATAREPORTING_PREF, false],
|
||||
reasonPref: [ReportBrokenSite.REASON_PREF, 0],
|
||||
sendMoreInfoPref: [ReportBrokenSite.SEND_MORE_INFO_PREF, false],
|
||||
newReportEndpointPref: [
|
||||
ReportBrokenSite.NEW_REPORT_ENDPOINT_PREF,
|
||||
DEFAULT_NEW_REPORT_ENDPOINT,
|
||||
],
|
||||
enabledPref: [ReportBrokenSite.REPORTER_ENABLED_PREF, true],
|
||||
reportSiteIssueEnabledPref: [
|
||||
ReportBrokenSite.REPORT_SITE_ISSUE_PREF,
|
||||
false,
|
||||
],
|
||||
})) {
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
name,
|
||||
pref,
|
||||
dflt,
|
||||
this.#checkPrefs.bind(this)
|
||||
);
|
||||
}
|
||||
this.#checkPrefs();
|
||||
}
|
||||
|
||||
canReportURI(uri) {
|
||||
return uri && (uri.schemeIs("http") || uri.schemeIs("https"));
|
||||
}
|
||||
|
||||
updateParentMenu(event) {
|
||||
// We need to make sure that the Report Broken Site menu item
|
||||
// is disabled and/or hidden depending on the prefs/active tab URL
|
||||
// when our parent popups are shown, and if their tab's location
|
||||
// changes while they are open.
|
||||
const tabbrowser = event.target.ownerGlobal.gBrowser;
|
||||
this.enableOrDisableMenuitems(tabbrowser.selectedBrowser);
|
||||
|
||||
tabbrowser.addTabsProgressListener(this);
|
||||
event.target.addEventListener(
|
||||
"popuphidden",
|
||||
() => {
|
||||
tabbrowser.removeTabsProgressListener(this);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
|
||||
init(tabbrowser) {
|
||||
// Called in browser.js.
|
||||
const { ownerGlobal } = tabbrowser.selectedBrowser;
|
||||
const { document } = ownerGlobal;
|
||||
|
||||
const state = ViewState.get(document);
|
||||
|
||||
this.#initMainView(state);
|
||||
this.#initReportSentView(state);
|
||||
|
||||
for (const id of ["menu_HelpPopup", "appMenu-popup"]) {
|
||||
document
|
||||
.getElementById(id)
|
||||
.addEventListener("popupshown", this.updateParentMenu.bind(this));
|
||||
}
|
||||
|
||||
ownerGlobal.PanelMultiView.getViewNode(
|
||||
document,
|
||||
ReportBrokenSite.MAIN_PANELVIEW_ID
|
||||
).addEventListener("ViewShowing", ({ target }) => {
|
||||
const { selectedBrowser } = target.ownerGlobal.gBrowser;
|
||||
let source = "helpMenu";
|
||||
switch (target.closest("panelmultiview")?.id) {
|
||||
case "appMenu-multiView":
|
||||
source = "hamburgerMenu";
|
||||
break;
|
||||
case "protections-popup-multiView":
|
||||
source = "ETPShieldIconMenu";
|
||||
break;
|
||||
}
|
||||
this.#onMainViewShown(source, selectedBrowser);
|
||||
});
|
||||
}
|
||||
|
||||
enableOrDisableMenuitems(selectedbrowser) {
|
||||
// Ensures that the various Report Broken Site menu items and
|
||||
// toolbar buttons are enabled/hidden when appropriate (and
|
||||
// also the Help menu's Report Site Issue item)/
|
||||
|
||||
const canReportUrl = this.canReportURI(selectedbrowser.currentURI);
|
||||
|
||||
const { document } = selectedbrowser.ownerGlobal;
|
||||
|
||||
const cmd = document.getElementById("cmd_reportBrokenSite");
|
||||
if (this.enabled) {
|
||||
cmd.setAttribute("hidden", "false"); // see bug 805653
|
||||
} else {
|
||||
cmd.setAttribute("hidden", "true");
|
||||
}
|
||||
if (canReportUrl) {
|
||||
cmd.removeAttribute("disabled");
|
||||
} else {
|
||||
cmd.setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
// Changes to the "hidden" and "disabled" state of the command aren't reliably
|
||||
// reflected on the main menu unless we open it twice, or do it manually.
|
||||
// (See bug 1864953).
|
||||
const mainmenuItem = document.getElementById("help_reportBrokenSite");
|
||||
if (mainmenuItem) {
|
||||
mainmenuItem.hidden = !this.enabled;
|
||||
mainmenuItem.disabled = !canReportUrl;
|
||||
}
|
||||
|
||||
// Report Site Issue is our older issue reporter, shown in the Help
|
||||
// menu on pre-release channels. We should hide it unless we're
|
||||
// disabled, at which point we should show it when available.
|
||||
const reportSiteIssue = document.getElementById("help_reportSiteIssue");
|
||||
if (reportSiteIssue) {
|
||||
reportSiteIssue.hidden = this.enabled || !this.reportSiteIssueEnabledPref;
|
||||
reportSiteIssue.disabled = !canReportUrl;
|
||||
}
|
||||
}
|
||||
|
||||
#checkPrefs(whichChanged) {
|
||||
// No breakage reports can be sent by Glean if it's disabled, so we also
|
||||
// disable the broken site reporter. We also have our own pref.
|
||||
this.#_enabled =
|
||||
Services.policies.isAllowed("feedbackCommands") &&
|
||||
this.dataReportingPref &&
|
||||
this.enabledPref;
|
||||
|
||||
this.#reasonEnabled = this.reasonPref == 1 || this.reasonPref == 2;
|
||||
this.#reasonIsOptional = this.reasonPref == 1;
|
||||
|
||||
this.#sendMoreInfoEnabled = this.sendMoreInfoPref;
|
||||
this.#newReportEndpoint = this.newReportEndpointPref;
|
||||
}
|
||||
|
||||
#initMainView(state) {
|
||||
state.sendButton.addEventListener("command", async ({ target }) => {
|
||||
const multiview = target.closest("panelmultiview");
|
||||
state.reportSentView.hidden = false;
|
||||
multiview.showSubView("report-broken-site-popup-reportSentView");
|
||||
state.reset();
|
||||
});
|
||||
|
||||
state.cancelButton.addEventListener("command", ({ target, view }) => {
|
||||
const multiview = target.closest("panelmultiview");
|
||||
view.ownerGlobal.PanelMultiView.forNode(multiview).hidePopup();
|
||||
state.reset();
|
||||
});
|
||||
|
||||
state.sendMoreInfoLink.addEventListener("click", async event => {
|
||||
event.preventDefault();
|
||||
const tabbrowser = event.view.ownerGlobal.gBrowser;
|
||||
const multiview = event.target.closest("panelmultiview");
|
||||
event.view.ownerGlobal.PanelMultiView.forNode(multiview).hidePopup();
|
||||
await this.#openWebCompatTab(tabbrowser);
|
||||
state.reset();
|
||||
});
|
||||
|
||||
state.urlInput.addEventListener("input", ({ target }) => {
|
||||
const newUrlValid = target.value && target.checkValidity();
|
||||
if (state.isURLValid != newUrlValid) {
|
||||
state.isURLValid = newUrlValid;
|
||||
state.enableOrDisableSendButton(
|
||||
this.#reasonEnabled,
|
||||
this.#reasonIsOptional,
|
||||
this.#descriptionIsOptional
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
state.descriptionInput.addEventListener("input", ({ target }) => {
|
||||
const newDescValid = gDescriptionCheckRE.test(target.value);
|
||||
if (state.isDescriptionValid != newDescValid) {
|
||||
state.isDescriptionValid = newDescValid;
|
||||
state.enableOrDisableSendButton(
|
||||
this.#reasonEnabled,
|
||||
this.#reasonIsOptional,
|
||||
this.#descriptionIsOptional
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const reasonDropdown = state.reasonInput;
|
||||
reasonDropdown.addEventListener("command", ({ target }) => {
|
||||
const choiceId = target.closest("menulist").selectedItem.id;
|
||||
const newValidity = choiceId !== ViewState.CHOOSE_A_REASON_OPT_ID;
|
||||
if (state.isReasonValid != newValidity) {
|
||||
state.isReasonValid = newValidity;
|
||||
state.enableOrDisableSendButton(
|
||||
this.#reasonEnabled,
|
||||
this.#reasonIsOptional,
|
||||
this.#descriptionIsOptional
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const menupopup = reasonDropdown.querySelector("menupopup");
|
||||
const onDropDownShowOrHide = ({ type }) => {
|
||||
// Hide "choose a reason" while the user has the reason dropdown open
|
||||
const shouldHide = type == "popupshowing";
|
||||
state.chooseAReasonOption.hidden = shouldHide;
|
||||
};
|
||||
menupopup.addEventListener("popupshowing", onDropDownShowOrHide);
|
||||
menupopup.addEventListener("popuphiding", onDropDownShowOrHide);
|
||||
}
|
||||
|
||||
#initReportSentView(state) {
|
||||
state.okayButton.addEventListener("command", ({ target, view }) => {
|
||||
const multiview = target.closest("panelmultiview");
|
||||
view.ownerGlobal.PanelMultiView.forNode(multiview).hidePopup();
|
||||
});
|
||||
}
|
||||
|
||||
async #onMainViewShown(source, selectedBrowser) {
|
||||
const { document } = selectedBrowser.ownerGlobal;
|
||||
|
||||
let didReset = false;
|
||||
const state = ViewState.get(document);
|
||||
const uri = selectedBrowser.currentURI;
|
||||
if (!state.isURLValid && !state.isDescriptionValid) {
|
||||
state.reset();
|
||||
didReset = true;
|
||||
} else if (!state.currentTabURI || !uri.equals(state.currentTabURI)) {
|
||||
state.reset();
|
||||
didReset = true;
|
||||
} else if (!state.url) {
|
||||
state.resetURLToCurrentTab();
|
||||
}
|
||||
|
||||
state.mainView.hidden = false;
|
||||
|
||||
state.sendMoreInfoLink.hidden = !this.#sendMoreInfoEnabled;
|
||||
|
||||
state.reasonInput.hidden = !this.#reasonEnabled;
|
||||
|
||||
state.reasonLabelRequired.hidden =
|
||||
!this.#reasonEnabled || this.#reasonIsOptional;
|
||||
state.reasonLabelOptional.hidden =
|
||||
!this.#reasonEnabled || !this.#reasonIsOptional;
|
||||
|
||||
state.descriptionLabelRequired.hidden = this.#descriptionIsOptional;
|
||||
state.descriptionLabelOptional.hidden = !this.#descriptionIsOptional;
|
||||
|
||||
state.enableOrDisableSendButton(
|
||||
this.#reasonEnabled,
|
||||
this.#reasonIsOptional,
|
||||
this.#descriptionIsOptional
|
||||
);
|
||||
|
||||
if (didReset || !state.currentTabWebcompatDetailsPromise) {
|
||||
state.currentTabWebcompatDetailsPromise = this.#queryActor(
|
||||
"GetWebCompatInfo",
|
||||
undefined,
|
||||
selectedBrowser
|
||||
).catch(err => {
|
||||
console.error("Report Broken Site: unexpected error", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async #queryActor(msg, params, browser) {
|
||||
const actor =
|
||||
browser.browsingContext.currentWindowGlobal.getActor("ReportBrokenSite");
|
||||
return actor.sendQuery(msg, params);
|
||||
}
|
||||
|
||||
async #loadTab(tabbrowser, url, triggeringPrincipal) {
|
||||
const tab = tabbrowser.addTab(url, {
|
||||
inBackground: false,
|
||||
triggeringPrincipal,
|
||||
});
|
||||
const expectedBrowser = tabbrowser.getBrowserForTab(tab);
|
||||
return new Promise(resolve => {
|
||||
const listener = {
|
||||
onLocationChange(browser, webProgress, request, uri, flags) {
|
||||
if (
|
||||
browser == expectedBrowser &&
|
||||
uri.spec == url &&
|
||||
webProgress.isTopLevel
|
||||
) {
|
||||
resolve(tab);
|
||||
tabbrowser.removeTabsProgressListener(listener);
|
||||
}
|
||||
},
|
||||
};
|
||||
tabbrowser.addTabsProgressListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
async #openWebCompatTab(tabbrowser) {
|
||||
const endpointUrl = this.sendMoreInfoEndpoint;
|
||||
const principal = Services.scriptSecurityManager.createNullPrincipal({});
|
||||
const tab = await this.#loadTab(tabbrowser, endpointUrl, principal);
|
||||
const { document } = tabbrowser.selectedBrowser.ownerGlobal;
|
||||
const { description, reason, url, currentTabWebcompatDetailsPromise } =
|
||||
ViewState.get(document);
|
||||
|
||||
return this.#queryActor(
|
||||
"SendDataToWebcompatCom",
|
||||
{
|
||||
reason,
|
||||
description,
|
||||
endpointUrl,
|
||||
reportUrl: url,
|
||||
reporterConfig: ReportBrokenSite.WEBCOMPAT_REPORTER_CONFIG,
|
||||
webcompatInfo: await currentTabWebcompatDetailsPromise,
|
||||
},
|
||||
tab.linkedBrowser
|
||||
).catch(err => {
|
||||
console.error("Report Broken Site: unexpected error", err);
|
||||
});
|
||||
}
|
||||
|
||||
open(event) {
|
||||
const { target } = event.sourceEvent;
|
||||
const { selectedBrowser } = event.view.ownerGlobal.gBrowser;
|
||||
const { ownerGlobal } = selectedBrowser;
|
||||
const { document } = ownerGlobal;
|
||||
|
||||
switch (target.id) {
|
||||
case "appMenu-report-broken-site-button":
|
||||
ownerGlobal.PanelUI.showSubView(
|
||||
ReportBrokenSite.MAIN_PANELVIEW_ID,
|
||||
target
|
||||
);
|
||||
break;
|
||||
case "protections-popup-report-broken-site-button":
|
||||
document
|
||||
.getElementById("protections-popup-multiView")
|
||||
.showSubView(ReportBrokenSite.MAIN_PANELVIEW_ID);
|
||||
break;
|
||||
case "help_reportBrokenSite":
|
||||
// hide the hamburger menu first, as we overlap with it.
|
||||
const appMenuPopup = document.getElementById("appMenu-popup");
|
||||
appMenuPopup?.hidePopup();
|
||||
|
||||
// See bug 1864957; we should be able to use showSubView here
|
||||
ownerGlobal.PanelMultiView.openPopup(
|
||||
document.getElementById("report-broken-main-menu-panel"),
|
||||
document.getElementById("PanelUI-menu-button"),
|
||||
{ position: "bottomright topright" }
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,120 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<panel id="report-broken-main-menu-panel"
|
||||
class="cui-widget-panel panel-no-padding"
|
||||
type="arrow"
|
||||
role="document"
|
||||
noautofocus="true"
|
||||
tabspecific="true"
|
||||
flip="slide"
|
||||
orient="vertical">
|
||||
<panelmultiview mainViewId="report-broken-site-popup-mainView"/>
|
||||
</panel>
|
||||
|
||||
<panelview id="report-broken-site-popup-mainView"
|
||||
class="report-broken-site-view main-view PanelUI-subView"
|
||||
role="document"
|
||||
data-l10n-id="report-broken-site-panel-header"
|
||||
data-l10n-attrs="title"
|
||||
hidden="true"
|
||||
showheader="true">
|
||||
<!-- We need to have a panel-header for the main/help menu, and some element
|
||||
inside of the header needs a .panel-info-button class for the back button
|
||||
to be shown when opened from the appmenu or protections panel, as per
|
||||
https://searchfox.org/mozilla-central/source/browser/components/customizableui/PanelMultiView.sys.mjs#1423
|
||||
-->
|
||||
<box class="panel-header">
|
||||
<html:h1 role="heading" aria-level="1" class="panel-info-button">
|
||||
<html:span data-l10n-id="report-broken-site-mainview-title"/>
|
||||
</html:h1>
|
||||
</box>
|
||||
<toolbarseparator></toolbarseparator>
|
||||
<vbox id="report-broken-site-panel-container"
|
||||
role="alertdialog"
|
||||
aria-labelledby="report-broken-site-panel-intro">
|
||||
<html:p data-l10n-id="report-broken-site-panel-intro"/>
|
||||
<label id="report-broken-site-popup-url-label"
|
||||
control="report-broken-site-popup-url"
|
||||
data-l10n-id="report-broken-site-panel-url"/>
|
||||
<html:input id="report-broken-site-popup-url"
|
||||
allow-arrow-navigation="true"
|
||||
aria-describedby="report-broken-site-panel-intro"
|
||||
type="url"/>
|
||||
<label id="report-broken-site-popup-reason-label"
|
||||
control="report-broken-site-popup-reason"
|
||||
hidden="true"
|
||||
data-l10n-attrs="aria-label"
|
||||
data-l10n-id="report-broken-site-panel-reason-label"/>
|
||||
<label id="report-broken-site-popup-reason-optional-label"
|
||||
control="report-broken-site-popup-reason"
|
||||
hidden="true"
|
||||
data-l10n-attrs="aria-label"
|
||||
data-l10n-id="report-broken-site-panel-reason-optional-label"/>
|
||||
<menulist id="report-broken-site-popup-reason"
|
||||
allow-arrow-navigation="true"
|
||||
class="plain">
|
||||
<menupopup>
|
||||
<menuitem id="report-broken-site-popup-reason-choose"
|
||||
data-l10n-id="report-broken-site-panel-reason-choose"/>
|
||||
<menuitem id="report-broken-site-popup-reason-slow"
|
||||
data-l10n-id="report-broken-site-panel-reason-slow"/>
|
||||
<menuitem id="report-broken-site-popup-reason-media"
|
||||
data-l10n-id="report-broken-site-panel-reason-media"/>
|
||||
<menuitem id="report-broken-site-popup-reason-content"
|
||||
data-l10n-id="report-broken-site-panel-reason-content"/>
|
||||
<menuitem id="report-broken-site-popup-reason-account"
|
||||
data-l10n-id="report-broken-site-panel-reason-account"/>
|
||||
<menuitem id="report-broken-site-popup-reason-adblockers"
|
||||
data-l10n-id="report-broken-site-panel-reason-adblockers"/>
|
||||
<menuitem id="report-broken-site-popup-reason-other"
|
||||
data-l10n-id="report-broken-site-panel-reason-other"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<label id="report-broken-site-popup-description-label"
|
||||
control="report-broken-site-popup-description"
|
||||
hidden="true"
|
||||
data-l10n-id="report-broken-site-panel-description-label"/>
|
||||
<label id="report-broken-site-popup-description-optional-label"
|
||||
control="report-broken-site-popup-description"
|
||||
hidden="true"
|
||||
data-l10n-id="report-broken-site-panel-description-optional-label"/>
|
||||
<html:textarea id="report-broken-site-popup-description"
|
||||
allow-arrow-navigation="true"
|
||||
rows="8"></html:textarea>
|
||||
<html:a id="report-broken-site-popup-send-more-info-link"
|
||||
href="#"
|
||||
data-l10n-id="report-broken-site-panel-send-more-info-link"/>
|
||||
<html:moz-button-group class="panel-footer">
|
||||
<button id="report-broken-site-popup-cancel-button"
|
||||
class="footer-button"
|
||||
data-l10n-id="report-broken-site-panel-button-cancel"/>
|
||||
<button id="report-broken-site-popup-send-button"
|
||||
class="footer-button primary"
|
||||
data-l10n-id="report-broken-site-panel-button-send"/>
|
||||
</html:moz-button-group>
|
||||
</vbox>
|
||||
</panelview>
|
||||
|
||||
<panelview id="report-broken-site-popup-reportSentView"
|
||||
class="report-broken-site-view sent-view PanelUI-subView"
|
||||
role="dialog"
|
||||
data-l10n-id="report-broken-site-panel-report-sent-header"
|
||||
data-l10n-attrs="title"
|
||||
hidden="true"
|
||||
showheader="true">
|
||||
<box class="panel-header">
|
||||
<html:h1 role="alert" aria-level="1">
|
||||
<html:span data-l10n-id="report-broken-site-panel-report-sent-label"/>
|
||||
</html:h1>
|
||||
</box>
|
||||
<vbox class="panel-subview-body subview-subheader">
|
||||
<html:p data-l10n-id="report-broken-site-panel-report-sent-text"/>
|
||||
<html:moz-button-group class="panel-footer">
|
||||
<button id="report-broken-site-popup-okay-button"
|
||||
class="footer-button primary"
|
||||
data-l10n-id="report-broken-site-panel-button-okay"/>
|
||||
</html:moz-button-group>
|
||||
</vbox>
|
||||
</panelview>
|
|
@ -0,0 +1,14 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ["test/browser/browser.toml"]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
"ReportBrokenSite.sys.mjs",
|
||||
]
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Web Compatibility", "Desktop")
|
|
@ -0,0 +1,18 @@
|
|||
[DEFAULT]
|
||||
tags = "report-broken-site"
|
||||
support-files = [
|
||||
"head.js",
|
||||
"sendMoreInfoTestEndpoint.html",
|
||||
]
|
||||
|
||||
["browser_parent_menuitems.js"]
|
||||
|
||||
["browser_reason_dropdown.js"]
|
||||
|
||||
["browser_report_send.js"]
|
||||
|
||||
["browser_report_site_issue_fallback.js"]
|
||||
|
||||
["browser_send_more_info.js"]
|
||||
|
||||
["browser_tab_switch_handling.js"]
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Test that the Report Broken Site menu items are disabled
|
||||
* when the active tab is not on a reportable URL, and is hidden
|
||||
* when the feature is disabled via pref.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function test() {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
const appMenu = AppMenu();
|
||||
const menus = [appMenu, ProtectionsPanel(), HelpMenu()];
|
||||
|
||||
async function forceMenuItemStateUpdate() {
|
||||
window.ReportBrokenSite.enableOrDisableMenuitems(window);
|
||||
|
||||
// the hidden/disabled state of all of the menuitems may not update until one
|
||||
// is rendered; then the related <command>'s state is propagated to them all.
|
||||
await appMenu.open();
|
||||
await appMenu.close();
|
||||
}
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:blank", async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden on invalid page when preffed off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden on valid page when preffed off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:blank", async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemDisabled(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option disabled on invalid page when preffed on`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemEnabled(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option enabled on valid page when preffed on`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden again when pref toggled back off`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the reason dropdown is shown or hidden
|
||||
* based on its pref, and that its optional and required modes affect
|
||||
* the Send button and report appropriately.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
async function clickSendAndCheckPing(rbs, expectedReason = null) {
|
||||
const pingCheck = new Promise(resolve => {
|
||||
GleanPings.brokenSiteReport.testBeforeNextSubmit(() => {
|
||||
Assert.equal(
|
||||
Glean.brokenSiteReport.breakageCategory.testGetValue(),
|
||||
expectedReason
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await rbs.clickSend();
|
||||
return pingCheck;
|
||||
}
|
||||
|
||||
add_task(async function testReasonDropdown() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
REPORTABLE_PAGE_URL,
|
||||
async function (browser) {
|
||||
ensureReasonDisabled();
|
||||
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonHidden();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs);
|
||||
await rbs.clickOkay();
|
||||
|
||||
ensureReasonOptional();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonOptional();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs);
|
||||
await rbs.clickOkay();
|
||||
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonOptional();
|
||||
rbs.chooseReason("slow");
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs, "slow");
|
||||
await rbs.clickOkay();
|
||||
|
||||
ensureReasonRequired();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonRequired();
|
||||
await rbs.isSendButtonDisabled();
|
||||
rbs.chooseReason("media");
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs, "media");
|
||||
await rbs.clickOkay();
|
||||
}
|
||||
);
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that sending or canceling reports with
|
||||
* the Send and Cancel buttons work (as well as the Okay button)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(10);
|
||||
|
||||
async function testSend(menu, url, description = "any") {
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
|
||||
if (!url) {
|
||||
url = menu.win.gBrowser.currentURI.spec;
|
||||
}
|
||||
|
||||
const pingCheck = new Promise(resolve => {
|
||||
GleanPings.brokenSiteReport.testBeforeNextSubmit(() => {
|
||||
Assert.equal(Glean.brokenSiteReport.url.testGetValue(), url);
|
||||
Assert.equal(
|
||||
Glean.brokenSiteReport.description.testGetValue(),
|
||||
description
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
await rbs.clickSend();
|
||||
await pingCheck;
|
||||
await rbs.clickOkay();
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
|
||||
async function testCancel(menu, url, description) {
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
await rbs.clickCancel();
|
||||
ok(!rbs.opened, "clicking Cancel closes Report Broken Site");
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(AppMenu());
|
||||
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(
|
||||
ProtectionsPanel(),
|
||||
"https://test.org/test/#fake",
|
||||
"test description"
|
||||
);
|
||||
|
||||
const win2 = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const tab3 = await openTab(REPORTABLE_PAGE_URL2, win2);
|
||||
|
||||
await testSend(AppMenu(win2), null, "another test description");
|
||||
|
||||
closeTab(tab3);
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
});
|
||||
|
||||
add_task(async function testCancelButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testCancel(AppMenu());
|
||||
await testCancel(ProtectionsPanel());
|
||||
await testCancel(HelpMenu());
|
||||
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testCancel(AppMenu());
|
||||
await testCancel(ProtectionsPanel());
|
||||
await testCancel(HelpMenu());
|
||||
|
||||
const win2 = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const tab3 = await openTab(REPORTABLE_PAGE_URL2, win2);
|
||||
|
||||
await testCancel(AppMenu(win2));
|
||||
await testCancel(ProtectionsPanel(win2));
|
||||
await testCancel(HelpMenu(win2));
|
||||
|
||||
closeTab(tab3);
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
});
|
|
@ -0,0 +1,89 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests that when Report Broken Site is active,
|
||||
* Report Site Issue is hidden.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
async function testDisabledByReportBrokenSite(menu) {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReportSiteIssuePreffedOn();
|
||||
|
||||
await menu.open();
|
||||
menu.isReportSiteIssueHidden();
|
||||
await menu.close();
|
||||
}
|
||||
|
||||
async function testDisabledByPref(menu) {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
ensureReportSiteIssuePreffedOff();
|
||||
|
||||
await menu.open();
|
||||
menu.isReportSiteIssueHidden();
|
||||
await menu.close();
|
||||
}
|
||||
|
||||
async function testDisabledForInvalidURLs(menu) {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
ensureReportSiteIssuePreffedOn();
|
||||
|
||||
await menu.open();
|
||||
menu.isReportSiteIssueDisabled();
|
||||
await menu.close();
|
||||
}
|
||||
|
||||
async function testEnabledForValidURLs(menu) {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
ensureReportSiteIssuePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
REPORTABLE_PAGE_URL,
|
||||
async function (browser) {
|
||||
await menu.open();
|
||||
menu.isReportSiteIssueEnabled();
|
||||
await menu.close();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// AppMenu help sub-menu
|
||||
|
||||
add_task(async function testDisabledByReportBrokenSiteAppMenuHelpSubmenu() {
|
||||
await testDisabledByReportBrokenSite(AppMenuHelpSubmenu());
|
||||
});
|
||||
|
||||
// disabled for now due to bug 1775402
|
||||
//add_task(async function testDisabledByPrefAppMenuHelpSubmenu() {
|
||||
// await testDisabledByPref(AppMenuHelpSubmenu());
|
||||
//});
|
||||
|
||||
add_task(async function testDisabledForInvalidURLsAppMenuHelpSubmenu() {
|
||||
await testDisabledForInvalidURLs(AppMenuHelpSubmenu());
|
||||
});
|
||||
|
||||
add_task(async function testEnabledForValidURLsAppMenuHelpSubmenu() {
|
||||
await testEnabledForValidURLs(AppMenuHelpSubmenu());
|
||||
});
|
||||
|
||||
// Help menu
|
||||
|
||||
add_task(async function testDisabledByReportBrokenSiteHelpMenu() {
|
||||
await testDisabledByReportBrokenSite(HelpMenu());
|
||||
});
|
||||
|
||||
// disabled for now due to bug 1775402
|
||||
//add_task(async function testDisabledByPrefHelpMenu() {
|
||||
// await testDisabledByPref(HelpMenu());
|
||||
//});
|
||||
|
||||
add_task(async function testDisabledForInvalidURLsHelpMenu() {
|
||||
await testDisabledForInvalidURLs(HelpMenu());
|
||||
});
|
||||
|
||||
add_task(async function testEnabledForValidURLsHelpMenu() {
|
||||
await testEnabledForValidURLs(HelpMenu());
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests that the send more info link appears only when its pref
|
||||
* is set to true, and that when clicked it will open a tab to
|
||||
* the webcompat.com endpoint and send the right data.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testSendMoreInfoPref() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
REPORTABLE_PAGE_URL,
|
||||
async function (browser) {
|
||||
await changeTab(gBrowser.selectedTab, REPORTABLE_PAGE_URL);
|
||||
|
||||
ensureSendMoreInfoDisabled();
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isSendMoreInfoHidden();
|
||||
await rbs.close();
|
||||
|
||||
ensureSendMoreInfoEnabled();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isSendMoreInfoShown();
|
||||
await rbs.close();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
async function testSendMoreInfo(menu, url, description = "any") {
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
|
||||
if (!url) {
|
||||
url = menu.win.gBrowser.currentURI.spec;
|
||||
}
|
||||
|
||||
const receivedData = await rbs.clickSendMoreInfo();
|
||||
const { message } = receivedData;
|
||||
|
||||
is(message.url, url, "sent correct URL");
|
||||
is(message.description, description, "sent correct description");
|
||||
|
||||
is(message.src, "desktop-reporter", "sent correct src");
|
||||
is(message.utm_campaign, "report-broken-site", "sent correct utm_campaign");
|
||||
is(message.utm_source, "desktop-reporter", "sent correct utm_source");
|
||||
|
||||
ok(typeof message.details == "object", "sent extra details");
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSendMoreInfo(AppMenu());
|
||||
|
||||
await changeTab(tab, REPORTABLE_PAGE_URL2);
|
||||
|
||||
await testSendMoreInfo(ProtectionsPanel(), "https://override.com", "another");
|
||||
|
||||
closeTab(tab);
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that Report Broken Site popups will be
|
||||
* reset to whichever tab the user is on as they change
|
||||
* between windows and tabs. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testResetsProperlyOnTabSwitch() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const badTab = await openTab("about:blank");
|
||||
const goodTab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
const goodTab2 = await openTab(REPORTABLE_PAGE_URL2);
|
||||
|
||||
const appMenu = AppMenu();
|
||||
const protPanel = ProtectionsPanel();
|
||||
|
||||
let rbs = await appMenu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
gBrowser.selectedTab = goodTab1;
|
||||
|
||||
rbs = await protPanel.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
gBrowser.selectedTab = badTab;
|
||||
await appMenu.open();
|
||||
appMenu.isReportBrokenSiteDisabled();
|
||||
await appMenu.close();
|
||||
|
||||
gBrowser.selectedTab = goodTab1;
|
||||
rbs = await protPanel.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
closeTab(badTab);
|
||||
closeTab(goodTab1);
|
||||
closeTab(goodTab2);
|
||||
});
|
||||
|
||||
add_task(async function testResetsProperlyOnWindowSwitch() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
const win2 = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL2, win2);
|
||||
|
||||
const appMenu1 = AppMenu();
|
||||
const appMenu2 = ProtectionsPanel(win2);
|
||||
|
||||
let rbs2 = await appMenu2.openReportBrokenSite();
|
||||
rbs2.isMainViewResetToCurrentTab();
|
||||
rbs2.close();
|
||||
|
||||
// flip back to tab1's window and ensure its URL pops up instead of tab2's URL
|
||||
await switchToWindow(window);
|
||||
isSelectedTab(window, tab1); // sanity check
|
||||
|
||||
let rbs1 = await appMenu1.openReportBrokenSite();
|
||||
rbs1.isMainViewResetToCurrentTab();
|
||||
rbs1.close();
|
||||
|
||||
// likewise flip back to tab2's window and ensure its URL pops up instead of tab1's URL
|
||||
await switchToWindow(win2);
|
||||
isSelectedTab(win2, tab2); // sanity check
|
||||
|
||||
rbs2 = await appMenu2.openReportBrokenSite();
|
||||
rbs2.isMainViewResetToCurrentTab();
|
||||
rbs2.close();
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
});
|
|
@ -0,0 +1,679 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { CustomizableUITestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/CustomizableUITestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const BASE_URL =
|
||||
"https://example.com/browser/browser/components/reportbrokensite/test/browser/";
|
||||
|
||||
const REPORTABLE_PAGE_URL = "https://example.com";
|
||||
|
||||
const REPORTABLE_PAGE_URL2 = REPORTABLE_PAGE_URL.replace(".com", ".org");
|
||||
|
||||
const NEW_REPORT_ENDPOINT_TEST_URL = `${BASE_URL}/sendMoreInfoTestEndpoint.html`;
|
||||
|
||||
const PREFS = {
|
||||
DATAREPORTING_ENABLED: "datareporting.healthreport.uploadEnabled",
|
||||
REPORTER_ENABLED: "ui.new-webcompat-reporter.enabled",
|
||||
REASON: "ui.new-webcompat-reporter.reason-dropdown",
|
||||
SEND_MORE_INFO: "ui.new-webcompat-reporter.send-more-info-link",
|
||||
NEW_REPORT_ENDPOINT: "ui.new-webcompat-reporter.new-report-endpoint",
|
||||
REPORT_SITE_ISSUE_ENABLED: "extensions.webcompat-reporter.enabled",
|
||||
};
|
||||
|
||||
function add_common_setup() {
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [[PREFS.NEW_REPORT_ENDPOINT, NEW_REPORT_ENDPOINT_TEST_URL]],
|
||||
});
|
||||
registerCleanupFunction(function () {
|
||||
for (const prefName of Object.values(PREFS)) {
|
||||
Services.prefs.clearUserPref(prefName);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function openTab(url, win) {
|
||||
const options = {
|
||||
gBrowser:
|
||||
win?.gBrowser ||
|
||||
Services.wm.getMostRecentWindow("navigator:browser").gBrowser,
|
||||
url,
|
||||
};
|
||||
return BrowserTestUtils.openNewForegroundTab(options);
|
||||
}
|
||||
|
||||
async function changeTab(tab, url) {
|
||||
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
function closeTab(tab) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
function switchToWindow(win) {
|
||||
const promises = [
|
||||
BrowserTestUtils.waitForEvent(win, "focus"),
|
||||
BrowserTestUtils.waitForEvent(win, "activate"),
|
||||
];
|
||||
win.focus();
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function isSelectedTab(win, tab) {
|
||||
const selectedTab = win.document.querySelector(".tabbrowser-tab[selected]");
|
||||
is(selectedTab, tab);
|
||||
}
|
||||
|
||||
function ensureReportBrokenSitePreffedOn() {
|
||||
Services.prefs.setBoolPref(PREFS.DATAREPORTING_ENABLED, true);
|
||||
Services.prefs.setBoolPref(PREFS.REPORTER_ENABLED, true);
|
||||
}
|
||||
|
||||
function ensureReportBrokenSitePreffedOff() {
|
||||
Services.prefs.setBoolPref(PREFS.REPORTER_ENABLED, false);
|
||||
}
|
||||
|
||||
function ensureReportSiteIssuePreffedOn() {
|
||||
Services.prefs.setBoolPref(PREFS.REPORT_SITE_ISSUE_ENABLED, true);
|
||||
}
|
||||
|
||||
function ensureReportSiteIssuePreffedOff() {
|
||||
Services.prefs.setBoolPref(PREFS.REPORT_SITE_ISSUE_ENABLED, false);
|
||||
}
|
||||
|
||||
function ensureSendMoreInfoEnabled() {
|
||||
Services.prefs.setBoolPref(PREFS.SEND_MORE_INFO, true);
|
||||
}
|
||||
|
||||
function ensureSendMoreInfoDisabled() {
|
||||
Services.prefs.setBoolPref(PREFS.SEND_MORE_INFO, false);
|
||||
}
|
||||
|
||||
function ensureReasonDisabled() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 0);
|
||||
}
|
||||
|
||||
function ensureReasonOptional() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 1);
|
||||
}
|
||||
|
||||
function ensureReasonRequired() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 2);
|
||||
}
|
||||
|
||||
function isMenuItemEnabled(menuItem, itemDesc) {
|
||||
ok(!menuItem.hidden, `${itemDesc} menu item is shown`);
|
||||
ok(!menuItem.disabled, `${itemDesc} menu item is enabled`);
|
||||
}
|
||||
|
||||
function isMenuItemHidden(menuItem, itemDesc) {
|
||||
ok(
|
||||
!menuItem || menuItem.hidden || !BrowserTestUtils.is_visible(menuItem),
|
||||
`${itemDesc} menu item is hidden`
|
||||
);
|
||||
}
|
||||
|
||||
function isMenuItemDisabled(menuItem, itemDesc) {
|
||||
ok(!menuItem.hidden, `${itemDesc} menu item is shown`);
|
||||
ok(menuItem.disabled, `${itemDesc} menu item is disabled`);
|
||||
}
|
||||
|
||||
class ReportBrokenSiteHelper {
|
||||
sourceMenu = undefined;
|
||||
win = undefined;
|
||||
|
||||
constructor(sourceMenu) {
|
||||
this.sourceMenu = sourceMenu;
|
||||
this.win = sourceMenu.win;
|
||||
}
|
||||
|
||||
get mainView() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-mainView"
|
||||
);
|
||||
}
|
||||
|
||||
get sentView() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-reportSentView"
|
||||
);
|
||||
}
|
||||
|
||||
get openPanel() {
|
||||
return this.mainView?.closest("panel");
|
||||
}
|
||||
|
||||
get opened() {
|
||||
return this.openPanel?.hasAttribute("panelopen");
|
||||
}
|
||||
|
||||
async open(triggerMenuItem) {
|
||||
const window = triggerMenuItem.ownerGlobal;
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"ViewShown",
|
||||
true,
|
||||
e => e.target.classList.contains("report-broken-site-view")
|
||||
);
|
||||
await EventUtils.synthesizeMouseAtCenter(triggerMenuItem, {}, window);
|
||||
await shownPromise;
|
||||
}
|
||||
|
||||
async #assertClickAndViewChanges(button, view, newView) {
|
||||
ok(view.closest("panel").hasAttribute("panelopen"), "Panel is open");
|
||||
ok(BrowserTestUtils.is_visible(button), "Button is visible");
|
||||
ok(!button.disabled, "Button is enabled");
|
||||
const promises = [];
|
||||
if (newView) {
|
||||
promises.push(BrowserTestUtils.waitForEvent(newView, "ViewShown"));
|
||||
} else {
|
||||
promises.push(BrowserTestUtils.waitForEvent(view, "ViewHiding"));
|
||||
}
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, this.win);
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async clickSend() {
|
||||
await this.#assertClickAndViewChanges(
|
||||
this.sendButton,
|
||||
this.mainView,
|
||||
this.sentView
|
||||
);
|
||||
}
|
||||
|
||||
async clickSendMoreInfo() {
|
||||
const newTabPromise = BrowserTestUtils.waitForNewTab(
|
||||
this.win.gBrowser,
|
||||
NEW_REPORT_ENDPOINT_TEST_URL
|
||||
);
|
||||
EventUtils.synthesizeMouseAtCenter(this.sendMoreInfoLink, {}, this.win);
|
||||
const newTab = await newTabPromise;
|
||||
const receivedData = await SpecialPowers.spawn(
|
||||
newTab.linkedBrowser,
|
||||
[],
|
||||
async function () {
|
||||
await content.wrappedJSObject.messageArrived;
|
||||
return content.wrappedJSObject.message;
|
||||
}
|
||||
);
|
||||
this.win.gBrowser.removeCurrentTab();
|
||||
return receivedData;
|
||||
}
|
||||
|
||||
async clickCancel() {
|
||||
await this.#assertClickAndViewChanges(this.cancelButton, this.mainView);
|
||||
}
|
||||
|
||||
async clickOkay() {
|
||||
await this.#assertClickAndViewChanges(this.okayButton, this.sentView);
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.opened) {
|
||||
this.openPanel?.hidePopup(false);
|
||||
}
|
||||
this.sourceMenu?.close();
|
||||
}
|
||||
|
||||
// UI element getters
|
||||
|
||||
get URLInput() {
|
||||
return this.win.document.getElementById("report-broken-site-popup-url");
|
||||
}
|
||||
|
||||
get reasonInput() {
|
||||
return this.win.document.getElementById("report-broken-site-popup-reason");
|
||||
}
|
||||
|
||||
get reasonLabelRequired() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-reason-label"
|
||||
);
|
||||
}
|
||||
|
||||
get reasonLabelOptional() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-reason-optional-label"
|
||||
);
|
||||
}
|
||||
|
||||
get descriptionTextarea() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-description"
|
||||
);
|
||||
}
|
||||
|
||||
get sendMoreInfoLink() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-send-more-info-link"
|
||||
);
|
||||
}
|
||||
|
||||
get sendButton() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-send-button"
|
||||
);
|
||||
}
|
||||
|
||||
get cancelButton() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-cancel-button"
|
||||
);
|
||||
}
|
||||
|
||||
get okayButton() {
|
||||
return this.win.document.getElementById(
|
||||
"report-broken-site-popup-okay-button"
|
||||
);
|
||||
}
|
||||
|
||||
// Test helpers
|
||||
|
||||
#setInput(input, value) {
|
||||
input.value = value;
|
||||
input.dispatchEvent(
|
||||
new UIEvent("input", { bubbles: true, view: this.win })
|
||||
);
|
||||
}
|
||||
|
||||
setURL(value) {
|
||||
this.#setInput(this.URLInput, value);
|
||||
}
|
||||
|
||||
chooseReason(value) {
|
||||
const item = this.win.document.getElementById(
|
||||
`report-broken-site-popup-reason-${value}`
|
||||
);
|
||||
const input = this.reasonInput;
|
||||
input.selectedItem = item;
|
||||
input.dispatchEvent(
|
||||
new UIEvent("command", { bubbles: true, view: this.win })
|
||||
);
|
||||
}
|
||||
|
||||
setDescription(value) {
|
||||
this.#setInput(this.descriptionTextarea, value);
|
||||
}
|
||||
|
||||
isURL(expected) {
|
||||
is(this.URLInput.value, expected);
|
||||
}
|
||||
|
||||
isSendButtonEnabled() {
|
||||
ok(BrowserTestUtils.is_visible(this.sendButton), "Send button is visible");
|
||||
ok(!this.sendButton.disabled, "Send button is enabled");
|
||||
}
|
||||
|
||||
isSendButtonDisabled() {
|
||||
ok(BrowserTestUtils.is_visible(this.sendButton), "Send button is visible");
|
||||
ok(this.sendButton.disabled, "Send button is disabled");
|
||||
}
|
||||
|
||||
isSendMoreInfoShown() {
|
||||
ok(
|
||||
BrowserTestUtils.is_visible(this.sendMoreInfoLink),
|
||||
"send more info is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isSendMoreInfoHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.is_visible(this.sendMoreInfoLink),
|
||||
"send more info is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isSendMoreInfoShownOrHiddenAppropriately() {
|
||||
if (Services.prefs.getBoolPref(PREFS.SEND_MORE_INFO)) {
|
||||
this.isSendMoreInfoShown();
|
||||
} else {
|
||||
this.isSendMoreInfoHidden();
|
||||
}
|
||||
}
|
||||
|
||||
isReasonHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.is_visible(this.reasonInput),
|
||||
"reason drop-down is hidden"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.is_visible(this.reasonLabelOptional),
|
||||
"optional reason label is hidden"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.is_visible(this.reasonLabelRequired),
|
||||
"required reason label is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonRequired() {
|
||||
ok(
|
||||
BrowserTestUtils.is_visible(this.reasonInput),
|
||||
"reason drop-down is shown"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.is_visible(this.reasonLabelOptional),
|
||||
"optional reason label is hidden"
|
||||
);
|
||||
ok(
|
||||
BrowserTestUtils.is_visible(this.reasonLabelRequired),
|
||||
"required reason label is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonOptional() {
|
||||
ok(
|
||||
BrowserTestUtils.is_visible(this.reasonInput),
|
||||
"reason drop-down is shown"
|
||||
);
|
||||
ok(
|
||||
BrowserTestUtils.is_visible(this.reasonLabelOptional),
|
||||
"optional reason label is shown"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.is_visible(this.reasonLabelRequired),
|
||||
"required reason label is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonShownOrHiddenAppropriately() {
|
||||
const pref = Services.prefs.getIntPref(PREFS.REASON);
|
||||
if (pref == 2) {
|
||||
this.isReasonOptional();
|
||||
} else if (pref == 1) {
|
||||
this.isReasonOptional();
|
||||
} else {
|
||||
this.isReasonHidden();
|
||||
}
|
||||
}
|
||||
|
||||
isDescription(expected) {
|
||||
return this.descriptionTextarea.value == expected;
|
||||
}
|
||||
|
||||
isMainViewResetToCurrentTab() {
|
||||
this.isURL(this.win.gBrowser.selectedBrowser.currentURI.spec);
|
||||
this.isDescription("");
|
||||
this.isReasonShownOrHiddenAppropriately();
|
||||
this.isSendMoreInfoShownOrHiddenAppropriately();
|
||||
}
|
||||
}
|
||||
|
||||
class MenuHelper {
|
||||
menuDescription = undefined;
|
||||
|
||||
win = undefined;
|
||||
|
||||
constructor(win = window) {
|
||||
this.win = win;
|
||||
}
|
||||
|
||||
get reportBrokenSite() {}
|
||||
|
||||
get reportSiteIssue() {}
|
||||
|
||||
get popup() {}
|
||||
|
||||
get opened() {
|
||||
return this.popup?.hasAttribute("panelopen");
|
||||
}
|
||||
|
||||
async open() {}
|
||||
|
||||
async close() {}
|
||||
|
||||
isReportBrokenSiteDisabled() {
|
||||
return isMenuItemDisabled(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportBrokenSiteEnabled() {
|
||||
return isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportBrokenSiteHidden() {
|
||||
return isMenuItemHidden(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportSiteIssueDisabled() {
|
||||
return isMenuItemDisabled(this.reportSiteIssue, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportSiteIssueEnabled() {
|
||||
return isMenuItemEnabled(this.reportSiteIssue, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportSiteIssueHidden() {
|
||||
return isMenuItemHidden(this.reportSiteIssue, this.menuDescription);
|
||||
}
|
||||
|
||||
async openReportBrokenSite() {
|
||||
if (!this.opened) {
|
||||
await this.open();
|
||||
}
|
||||
isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
const rbs = new ReportBrokenSiteHelper(this);
|
||||
await rbs.open(this.reportBrokenSite);
|
||||
return rbs;
|
||||
}
|
||||
|
||||
async openAndPrefillReportBrokenSite(url = null, description = "") {
|
||||
let rbs = await this.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
if (url) {
|
||||
rbs.setURL(url);
|
||||
}
|
||||
if (description) {
|
||||
rbs.setDescription(description);
|
||||
}
|
||||
return rbs;
|
||||
}
|
||||
}
|
||||
|
||||
class AppMenuHelper extends MenuHelper {
|
||||
menuDescription = "AppMenu";
|
||||
|
||||
get reportBrokenSite() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"appMenu-report-broken-site-button"
|
||||
);
|
||||
}
|
||||
|
||||
get reportSiteIssue() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get popup() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"appMenu-popup"
|
||||
);
|
||||
}
|
||||
|
||||
async open() {
|
||||
await new CustomizableUITestUtils(this.win).openMainMenu();
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.opened) {
|
||||
await new CustomizableUITestUtils(this.win).hideMainMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AppMenuHelpSubmenuHelper extends MenuHelper {
|
||||
menuDescription = "AppMenu help sub-menu";
|
||||
|
||||
get reportBrokenSite() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"appMenu_help_reportBrokenSite"
|
||||
);
|
||||
}
|
||||
|
||||
get reportSiteIssue() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"appMenu_help_reportSiteIssue"
|
||||
);
|
||||
}
|
||||
|
||||
get popup() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"appMenu-popup"
|
||||
);
|
||||
}
|
||||
|
||||
async open() {
|
||||
await new CustomizableUITestUtils(this.win).openMainMenu();
|
||||
|
||||
const anchor = this.win.document.getElementById("PanelUI-menu-button");
|
||||
this.win.PanelUI.showHelpView(anchor);
|
||||
|
||||
const appMenuHelpSubview =
|
||||
this.win.document.getElementById("PanelUI-helpView");
|
||||
await BrowserTestUtils.waitForEvent(appMenuHelpSubview, "ViewShowing");
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.opened) {
|
||||
await new CustomizableUITestUtils(this.win).hideMainMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HelpMenuHelper extends MenuHelper {
|
||||
menuDescription = "Help Menu";
|
||||
|
||||
get reportBrokenSite() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"help_reportBrokenSite"
|
||||
);
|
||||
}
|
||||
|
||||
get reportSiteIssue() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"help_reportSiteIssue"
|
||||
);
|
||||
}
|
||||
|
||||
get popup() {
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"menu_HelpPopup"
|
||||
);
|
||||
}
|
||||
|
||||
async open() {
|
||||
const popup = this.popup;
|
||||
const promise = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
|
||||
// This event-faking method was copied from browser_title_case_menus.js so
|
||||
// this can be tested on MacOS (where the actual menus cannot be opened in
|
||||
// tests, but we only need the help menu to populate itself).
|
||||
popup.dispatchEvent(new MouseEvent("popupshowing", { bubbles: true }));
|
||||
popup.dispatchEvent(new MouseEvent("popupshown", { bubbles: true }));
|
||||
|
||||
await promise;
|
||||
}
|
||||
|
||||
async close() {
|
||||
const popup = this.popup;
|
||||
const promise = BrowserTestUtils.waitForEvent(popup, "popuphidden");
|
||||
|
||||
// (Also copied from browser_title_case_menus.js)
|
||||
// Just for good measure, we'll fire the popuphiding/popuphidden events
|
||||
// after we close the menupopups.
|
||||
popup.dispatchEvent(new MouseEvent("popuphiding", { bubbles: true }));
|
||||
popup.dispatchEvent(new MouseEvent("popuphidden", { bubbles: true }));
|
||||
|
||||
await promise;
|
||||
}
|
||||
|
||||
async openReportBrokenSite() {
|
||||
// since we can't open the help menu on all OSes, we just directly open RBS instead.
|
||||
await this.open();
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(
|
||||
this.win,
|
||||
"ViewShown",
|
||||
true,
|
||||
e => e.target.classList.contains("report-broken-site-view")
|
||||
);
|
||||
ReportBrokenSite.open({
|
||||
sourceEvent: {
|
||||
target: this.reportBrokenSite,
|
||||
},
|
||||
view: this.win,
|
||||
});
|
||||
await shownPromise;
|
||||
return new ReportBrokenSiteHelper(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ProtectionsPanelHelper extends MenuHelper {
|
||||
menuDescription = "Protections Panel";
|
||||
|
||||
get reportBrokenSite() {
|
||||
this.win.gProtectionsHandler._initializePopup();
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"protections-popup-report-broken-site-button"
|
||||
);
|
||||
}
|
||||
|
||||
get reportSiteIssue() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get popup() {
|
||||
this.win.gProtectionsHandler._initializePopup();
|
||||
return this.win.PanelMultiView.getViewNode(
|
||||
this.win.document,
|
||||
"protections-popup"
|
||||
);
|
||||
}
|
||||
|
||||
async open() {
|
||||
const promise = BrowserTestUtils.waitForEvent(
|
||||
this.win,
|
||||
"popupshown",
|
||||
true,
|
||||
e => e.target.id == "protections-popup"
|
||||
);
|
||||
this.win.gProtectionsHandler.showProtectionsPopup();
|
||||
await promise;
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.opened) {
|
||||
const popup = this.popup;
|
||||
const promise = BrowserTestUtils.waitForEvent(popup, "popuphidden");
|
||||
this.win.PanelMultiView.hidePopup(popup, false);
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AppMenu(win = window) {
|
||||
return new AppMenuHelper(win);
|
||||
}
|
||||
|
||||
function AppMenuHelpSubmenu(win = window) {
|
||||
return new AppMenuHelpSubmenuHelper(win);
|
||||
}
|
||||
|
||||
function HelpMenu(win = window) {
|
||||
return new HelpMenuHelper(win);
|
||||
}
|
||||
|
||||
function ProtectionsPanel(win = window) {
|
||||
return new ProtectionsPanelHelper(win);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!-- 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/. -->
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let ready;
|
||||
window.wrtReady = new Promise(r => ready = r);
|
||||
|
||||
let arrived;
|
||||
window.messageArrived = new Promise(r => arrived = r);
|
||||
|
||||
window.addEventListener("message", e => {
|
||||
window.message = e.data;
|
||||
arrived();
|
||||
});
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
setTimeout(ready, 100);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -23,6 +23,7 @@
|
|||
rgba(207, 207, 216, .40)
|
||||
);
|
||||
--button-color: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
|
||||
--button-color-inverted: light-dark(rgb(251, 251, 254), rgb(21, 20, 26));
|
||||
|
||||
--focus-outline-color: var(--button-primary-bgcolor);
|
||||
|
||||
|
|
|
@ -2012,3 +2012,147 @@ panelview:not([mainview]) #PanelUI-whatsNew-title {
|
|||
#reset-pbm-panel-checkbox {
|
||||
margin-inline: 0 8px;
|
||||
}
|
||||
|
||||
/* Report Broken Site panels */
|
||||
|
||||
.report-broken-site-view .panel-subview-body * {
|
||||
font: menu;
|
||||
}
|
||||
|
||||
.report-broken-site-view button {
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.report-broken-site-view p {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.report-broken-site-view.main-view p {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
.report-broken-site-view .deemphasized,
|
||||
.report-broken-site-view .missing {
|
||||
color: inherit;
|
||||
font-size: 96%;
|
||||
}
|
||||
|
||||
#report-broken-site-popup-reason,
|
||||
#report-broken-site-popup-description {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
|
||||
.report-broken-site-view p.missing {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.report-broken-site-view p.missing {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.report-broken-site-view menulist {
|
||||
padding-inline-start: 0.25em;
|
||||
}
|
||||
|
||||
.report-broken-site-view input,
|
||||
.report-broken-site-view textarea {
|
||||
border: 1px solid var(--panel-separator-color);
|
||||
background-color: var(--button-color-inverted);
|
||||
margin: 0;
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.report-broken-site-view menulist {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
.report-broken-site-view input:disabled {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.report-broken-site-view.main-view #report-broken-site-panel-container > label {
|
||||
margin-block: 1.5em 0.5em;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
#report-broken-site-panel-container > hbox,
|
||||
#report-broken-site-panel-container > a {
|
||||
margin-block: 1.5em;
|
||||
}
|
||||
|
||||
.report-broken-site-view p + a {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
.report-broken-site-view {
|
||||
max-width: 26em;
|
||||
}
|
||||
|
||||
#report-broken-site-popup-url-label {
|
||||
margin-block-start: 1em;
|
||||
}
|
||||
|
||||
.report-broken-site-view button[disabled="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#report-broken-site-panel-container {
|
||||
margin: var(--arrowpanel-menuitem-margin);
|
||||
padding: var(--arrowpanel-menuitem-padding);
|
||||
}
|
||||
|
||||
#report-broken-site-panel-container > p,
|
||||
#report-broken-site-panel-container > label,
|
||||
#report-broken-site-panel-container > input,
|
||||
#report-broken-site-panel-container > textarea,
|
||||
#report-broken-site-panel-container > option {
|
||||
color: var(--panel-color);
|
||||
background: var(--panel-background);
|
||||
}
|
||||
|
||||
.report-broken-site-view.sent-view .panel-header {
|
||||
-moz-context-properties: fill;
|
||||
fill: #2ac3a2;
|
||||
background-image: url("chrome://global/skin/icons/check-filled.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: calc(var(--panel-separator-margin-horizontal) +
|
||||
var(--arrowpanel-menuitem-padding-inline)) center;
|
||||
}
|
||||
|
||||
.report-broken-site-view.sent-view .subviewbutton-back {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.report-broken-site-view.sent-view {
|
||||
background-color: var(--color-background-success);
|
||||
}
|
||||
|
||||
.report-broken-site-view moz-button-group.panel-footer {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.report-broken-site-view moz-button-group > button {
|
||||
background-color: var(--button-bgcolor);
|
||||
color: var(--button-color);
|
||||
}
|
||||
|
||||
.report-broken-site-view moz-button-group > button:hover {
|
||||
background-color: var(--button-hover-bgcolor);
|
||||
}
|
||||
|
||||
.report-broken-site-view moz-button-group > button:active {
|
||||
background-color: var(--button-active-bgcolor);
|
||||
}
|
||||
|
||||
.report-broken-site-view moz-button-group > button.primary {
|
||||
background-color: var(--button-primary-bgcolor);
|
||||
color: var(--button-primary-color);
|
||||
}
|
||||
|
||||
.report-broken-site-view moz-button-group > button.primary:hover {
|
||||
background-color: var(--button-primary-hover-bgcolor);
|
||||
}
|
||||
|
||||
.report-broken-site-view moz-button-group > button.primary:active {
|
||||
background-color: var(--button-primary-active-bgcolor);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ DIRS += [
|
|||
"remotebrowserutils",
|
||||
"reflect",
|
||||
"reputationservice",
|
||||
"reportbrokensite",
|
||||
"resistfingerprinting",
|
||||
"search",
|
||||
"sessionstore",
|
||||
|
|
|
@ -1929,6 +1929,30 @@ backgroundThreads:
|
|||
type: boolean
|
||||
setPref: threads.lower_mainthread_priority_in_background.enabled
|
||||
|
||||
reportBrokenSite:
|
||||
description: the Report Broken Site feature
|
||||
hasExposure: false
|
||||
isEarlyStartup: true
|
||||
variables:
|
||||
enabled:
|
||||
type: boolean
|
||||
setPref: ui.new-webcompat-reporter.enabled
|
||||
description: >-
|
||||
Whether Report Broken Site is enabled
|
||||
sendMoreInfo:
|
||||
type: boolean
|
||||
setPref: ui.new-webcompat-reporter.send-more-info-link
|
||||
description: >-
|
||||
Whether Report Broken Site shows the send more info link directing
|
||||
users to webcompat.com (defaults to true for prerelease channels)
|
||||
reasonDropdown:
|
||||
type: int
|
||||
setPref: ui.new-webcompat-reporter.reason-dropdown
|
||||
description: >-
|
||||
0 = do not show the "reason" dropdown
|
||||
1 = show an optional "reason" dropdown
|
||||
2 = show a required "reason" dropdown
|
||||
|
||||
feltPrivacy:
|
||||
description: Prefs for Felt Privacy v1 experiments
|
||||
owner: cmeador@mozilla.com
|
||||
|
|
|
@ -0,0 +1,558 @@
|
|||
/* 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/. */
|
||||
|
||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||
|
||||
const SCREENSHOT_FORMAT = { format: "jpeg", quality: 75 };
|
||||
|
||||
function RunScriptInFrame(win, script) {
|
||||
const contentPrincipal = win.document.nodePrincipal;
|
||||
const sandbox = Cu.Sandbox([contentPrincipal], {
|
||||
sandboxName: "Report Broken Site webcompat.com helper",
|
||||
sandboxPrototype: win,
|
||||
sameZoneAs: win,
|
||||
originAttributes: contentPrincipal.originAttributes,
|
||||
});
|
||||
return Cu.evalInSandbox(script, sandbox, null, "sandbox eval code", 1);
|
||||
}
|
||||
|
||||
class ConsoleLogHelper {
|
||||
static PREVIEW_MAX_ITEMS = 10;
|
||||
static LOG_LEVELS = ["debug", "info", "warn", "error"];
|
||||
|
||||
#windowId = undefined;
|
||||
|
||||
constructor(windowId) {
|
||||
this.#windowId = windowId;
|
||||
}
|
||||
|
||||
getLoggedMessages(alsoIncludePrivate = true) {
|
||||
return this.getConsoleAPIMessages().concat(
|
||||
this.getScriptErrors(alsoIncludePrivate)
|
||||
);
|
||||
}
|
||||
|
||||
getConsoleAPIMessages() {
|
||||
const ConsoleAPIStorage = Cc[
|
||||
"@mozilla.org/consoleAPI-storage;1"
|
||||
].getService(Ci.nsIConsoleAPIStorage);
|
||||
let messages = ConsoleAPIStorage.getEvents(this.#windowId);
|
||||
return messages.map(evt => {
|
||||
const { columnNumber, filename, level, lineNumber, timeStamp } = evt;
|
||||
|
||||
const args = [];
|
||||
for (const arg of evt.arguments) {
|
||||
args.push(this.#getArgs(arg));
|
||||
}
|
||||
|
||||
const message = {
|
||||
level,
|
||||
log: args,
|
||||
uri: filename,
|
||||
pos: `${lineNumber}:${columnNumber}`,
|
||||
};
|
||||
|
||||
return { timeStamp, message };
|
||||
});
|
||||
}
|
||||
|
||||
getScriptErrors(alsoIncludePrivate) {
|
||||
const messages = Services.console.getMessageArray();
|
||||
return messages
|
||||
.filter(message => {
|
||||
if (message instanceof Ci.nsIScriptError) {
|
||||
if (!alsoIncludePrivate && message.isFromPrivateWindow) {
|
||||
return false;
|
||||
}
|
||||
if (this.#windowId && this.#windowId !== message.innerWindowID) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If this is not an nsIScriptError and we need to do window-based
|
||||
// filtering we skip this message.
|
||||
return false;
|
||||
})
|
||||
.map(error => {
|
||||
const {
|
||||
timeStamp,
|
||||
errorMessage,
|
||||
sourceName,
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
logLevel,
|
||||
} = error;
|
||||
const message = {
|
||||
level: ConsoleLogHelper.LOG_LEVELS[logLevel],
|
||||
log: [errorMessage],
|
||||
uri: sourceName,
|
||||
pos: `${lineNumber}:${columnNumber}`,
|
||||
};
|
||||
return { timeStamp, message };
|
||||
});
|
||||
}
|
||||
|
||||
#getPreview(value) {
|
||||
switch (typeof value) {
|
||||
case "symbol":
|
||||
return value.toString();
|
||||
|
||||
case "function":
|
||||
return "function ()";
|
||||
|
||||
case "object":
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return `(${value.length})[...]`;
|
||||
}
|
||||
return "{...}";
|
||||
|
||||
case "undefined":
|
||||
return "undefined";
|
||||
|
||||
default:
|
||||
try {
|
||||
structuredClone(value);
|
||||
} catch (_) {
|
||||
return `${value}` || "?";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
#getArrayPreview(arr) {
|
||||
const preview = [];
|
||||
let count = 0;
|
||||
for (const value of arr) {
|
||||
if (++count > ConsoleLogHelper.PREVIEW_MAX_ITEMS) {
|
||||
break;
|
||||
}
|
||||
preview.push(this.#getPreview(value));
|
||||
}
|
||||
|
||||
return preview;
|
||||
}
|
||||
|
||||
#getObjectPreview(obj) {
|
||||
const preview = {};
|
||||
let count = 0;
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (++count > ConsoleLogHelper.PREVIEW_MAX_ITEMS) {
|
||||
break;
|
||||
}
|
||||
preview[key] = this.#getPreview(obj[key]);
|
||||
}
|
||||
|
||||
return preview;
|
||||
}
|
||||
|
||||
#getArgs(value) {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (Array.isArray(value)) {
|
||||
return this.#getArrayPreview(value);
|
||||
}
|
||||
return this.#getObjectPreview(value);
|
||||
}
|
||||
|
||||
return this.#getPreview(value);
|
||||
}
|
||||
}
|
||||
|
||||
const FrameworkDetector = {
|
||||
hasFastClickPageScript(window) {
|
||||
if (window.FastClick) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const property in window) {
|
||||
try {
|
||||
const proto = window[property].prototype;
|
||||
if (proto && proto.needsClick) {
|
||||
return true;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
hasMobifyPageScript(window) {
|
||||
return !!window.Mobify?.Tag;
|
||||
},
|
||||
|
||||
hasMarfeelPageScript(window) {
|
||||
return !!window.marfeel;
|
||||
},
|
||||
|
||||
checkWindow(window) {
|
||||
const script = `
|
||||
(function() {
|
||||
function ${FrameworkDetector.hasFastClickPageScript};
|
||||
function ${FrameworkDetector.hasMobifyPageScript};
|
||||
function ${FrameworkDetector.hasMarfeelPageScript};
|
||||
const win = window.wrappedJSObject || window;
|
||||
return {
|
||||
fastclick: hasFastClickPageScript(win),
|
||||
mobify: hasMobifyPageScript(win),
|
||||
marfeel: hasMarfeelPageScript(win),
|
||||
}
|
||||
})();
|
||||
`;
|
||||
return RunScriptInFrame(window, script);
|
||||
},
|
||||
};
|
||||
|
||||
function getSysinfoProperty(propertyName, defaultValue) {
|
||||
try {
|
||||
return Services.sysinfo.getProperty(propertyName);
|
||||
} catch (e) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function limitStringToLength(str, maxLength) {
|
||||
if (typeof str !== "string") {
|
||||
return null;
|
||||
}
|
||||
return str.substring(0, maxLength);
|
||||
}
|
||||
|
||||
const BrowserInfo = {
|
||||
getAppInfo() {
|
||||
const { userAgent } = Cc[
|
||||
"@mozilla.org/network/protocol;1?name=http"
|
||||
].getService(Ci.nsIHttpProtocolHandler);
|
||||
return {
|
||||
applicationName: Services.appinfo.name,
|
||||
buildId: Services.appinfo.appBuildID,
|
||||
defaultUserAgent: userAgent,
|
||||
updateChannel: AppConstants.MOZ_UPDATE_CHANNEL,
|
||||
version: Services.appinfo.version,
|
||||
};
|
||||
},
|
||||
|
||||
getPrefs() {
|
||||
const prefs = {};
|
||||
for (const [name, dflt] of Object.entries({
|
||||
"layers.acceleration.force-enabled": undefined,
|
||||
"gfx.webrender.software": undefined,
|
||||
"browser.opaqueResponseBlocking": undefined,
|
||||
"extensions.InstallTrigger.enabled": undefined,
|
||||
"privacy.resistFingerprinting": undefined,
|
||||
"privacy.globalprivacycontrol.enabled": undefined,
|
||||
})) {
|
||||
prefs[name] = Services.prefs.getBoolPref(name, dflt);
|
||||
}
|
||||
const cookieBehavior = "network.cookie.cookieBehavior";
|
||||
prefs[cookieBehavior] = Services.prefs.getIntPref(cookieBehavior);
|
||||
return prefs;
|
||||
},
|
||||
|
||||
getPlatformInfo() {
|
||||
let memoryMB = getSysinfoProperty("memsize", null);
|
||||
if (memoryMB) {
|
||||
memoryMB = Math.round(memoryMB / 1024 / 1024);
|
||||
}
|
||||
|
||||
const info = {
|
||||
fissionEnabled: Services.appinfo.fissionAutostart,
|
||||
memoryMB,
|
||||
osArchitecture: getSysinfoProperty("arch", null),
|
||||
osName: getSysinfoProperty("name", null),
|
||||
osVersion: getSysinfoProperty("version", null),
|
||||
os: AppConstants.platform,
|
||||
};
|
||||
if (AppConstants.platform === "android") {
|
||||
info.device = getSysinfoProperty("device", null);
|
||||
info.isTablet = getSysinfoProperty("tablet", false);
|
||||
}
|
||||
return info;
|
||||
},
|
||||
|
||||
getSecurityInfo() {
|
||||
if (AppConstants.platform != "win") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const maxStringLength = 256;
|
||||
|
||||
const keys = [
|
||||
["registeredAntiVirus", "antivirus"],
|
||||
["registeredAntiSpyware", "antispyware"],
|
||||
["registeredFirewall", "firewall"],
|
||||
];
|
||||
|
||||
let result = {};
|
||||
|
||||
for (let [inKey, outKey] of keys) {
|
||||
let prop = getSysinfoProperty(inKey, null);
|
||||
if (prop) {
|
||||
prop = limitStringToLength(prop, maxStringLength).split(";");
|
||||
}
|
||||
|
||||
result[outKey] = prop;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
getAllData() {
|
||||
return {
|
||||
app: BrowserInfo.getAppInfo(),
|
||||
prefs: BrowserInfo.getPrefs(),
|
||||
platform: BrowserInfo.getPlatformInfo(),
|
||||
security: BrowserInfo.getSecurityInfo(),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export class ReportBrokenSiteChild extends JSWindowActorChild {
|
||||
#getWebCompatInfo(docShell) {
|
||||
return Promise.all([
|
||||
this.#getConsoleLogs(docShell),
|
||||
this.sendQuery(
|
||||
"GetWebcompatInfoOnlyAvailableInParentProcess",
|
||||
SCREENSHOT_FORMAT
|
||||
),
|
||||
]).then(([consoleLog, infoFromParent]) => {
|
||||
const { antitracking, graphics, locales, screenshot } = infoFromParent;
|
||||
|
||||
const browser = BrowserInfo.getAllData();
|
||||
browser.graphics = graphics;
|
||||
browser.locales = locales;
|
||||
|
||||
const win = docShell.domWindow;
|
||||
const frameworks = FrameworkDetector.checkWindow(win);
|
||||
|
||||
if (browser.platform.os !== "linux") {
|
||||
delete browser.prefs["layers.acceleration.force-enabled"];
|
||||
}
|
||||
|
||||
return {
|
||||
antitracking,
|
||||
browser,
|
||||
consoleLog,
|
||||
devicePixelRatio: win.devicePixelRatio,
|
||||
frameworks,
|
||||
languages: win.navigator.languages,
|
||||
screenshot,
|
||||
url: win.location.href,
|
||||
userAgent: win.navigator.userAgent,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async #getConsoleLogs(docShell) {
|
||||
return this.#getLoggedMessages()
|
||||
.flat()
|
||||
.sort((a, b) => a.timeStamp - b.timeStamp)
|
||||
.map(m => m.message);
|
||||
}
|
||||
|
||||
#getLoggedMessages(alsoIncludePrivate = false) {
|
||||
const windowId = this.contentWindow.windowGlobalChild.innerWindowId;
|
||||
const helper = new ConsoleLogHelper(windowId, alsoIncludePrivate);
|
||||
return helper.getLoggedMessages();
|
||||
}
|
||||
|
||||
#formatReportDataForWebcompatCom({
|
||||
reason,
|
||||
description,
|
||||
reportUrl,
|
||||
reporterConfig,
|
||||
webcompatInfo,
|
||||
}) {
|
||||
const extra_labels = [];
|
||||
|
||||
const message = Object.assign({}, reporterConfig, {
|
||||
url: reportUrl,
|
||||
category: reason,
|
||||
description,
|
||||
details: {},
|
||||
extra_labels,
|
||||
});
|
||||
|
||||
const payload = {
|
||||
message,
|
||||
};
|
||||
|
||||
if (webcompatInfo) {
|
||||
const {
|
||||
antitracking,
|
||||
browser,
|
||||
devicePixelRatio,
|
||||
consoleLog,
|
||||
frameworks,
|
||||
languages,
|
||||
screenshot,
|
||||
url,
|
||||
userAgent,
|
||||
} = webcompatInfo;
|
||||
|
||||
const {
|
||||
blockList,
|
||||
isPrivateBrowsing,
|
||||
hasMixedActiveContentBlocked,
|
||||
hasMixedDisplayContentBlocked,
|
||||
hasTrackingContentBlocked,
|
||||
} = antitracking;
|
||||
|
||||
message.blockList = blockList;
|
||||
|
||||
const { app, graphics, prefs, platform, security } = browser;
|
||||
|
||||
const { applicationName, version, updateChannel, defaultUserAgent } = app;
|
||||
|
||||
const {
|
||||
fissionEnabled,
|
||||
memoryMb,
|
||||
osArchitecture,
|
||||
osName,
|
||||
osVersion,
|
||||
device,
|
||||
isTablet,
|
||||
} = platform;
|
||||
|
||||
const additionalData = {
|
||||
applicationName,
|
||||
blockList,
|
||||
devicePixelRatio,
|
||||
finalUserAgent: userAgent,
|
||||
fissionEnabled,
|
||||
gfxData: graphics,
|
||||
hasMixedActiveContentBlocked,
|
||||
hasMixedDisplayContentBlocked,
|
||||
hasTrackingContentBlocked,
|
||||
isPB: isPrivateBrowsing,
|
||||
languages,
|
||||
memoryMb,
|
||||
osArchitecture,
|
||||
osName,
|
||||
osVersion,
|
||||
prefs,
|
||||
updateChannel,
|
||||
userAgent: defaultUserAgent,
|
||||
version,
|
||||
};
|
||||
if (security !== undefined) {
|
||||
additionalData.sec = security;
|
||||
}
|
||||
if (device !== undefined) {
|
||||
additionalData.device = device;
|
||||
}
|
||||
if (isTablet !== undefined) {
|
||||
additionalData.isTablet = isTablet;
|
||||
}
|
||||
|
||||
const specialPrefs = {};
|
||||
for (const pref of [
|
||||
"layers.acceleration.force-enabled",
|
||||
"gfx.webrender.software",
|
||||
]) {
|
||||
specialPrefs[pref] = prefs[pref];
|
||||
}
|
||||
|
||||
const details = Object.assign(message.details, specialPrefs, {
|
||||
additionalData,
|
||||
buildId: browser.buildId,
|
||||
blockList,
|
||||
channel: browser.updateChannel,
|
||||
hasTouchScreen: browser.graphics.hasTouchScreen,
|
||||
});
|
||||
|
||||
// If the user enters a URL unrelated to the current tab,
|
||||
// don't bother sending a screnshot or logs/etc
|
||||
let sendRecordedPageSpecificDetails = false;
|
||||
try {
|
||||
const givenUri = new URL(reportUrl);
|
||||
const recordedUri = new URL(url);
|
||||
sendRecordedPageSpecificDetails =
|
||||
givenUri.origin == recordedUri.origin &&
|
||||
givenUri.pathname == recordedUri.pathname;
|
||||
} catch (_) {}
|
||||
|
||||
if (sendRecordedPageSpecificDetails) {
|
||||
payload.screenshot = screenshot;
|
||||
|
||||
details.consoleLog = consoleLog;
|
||||
details.frameworks = frameworks;
|
||||
details["mixed active content blocked"] =
|
||||
antitracking.hasMixedActiveContentBlocked;
|
||||
details["mixed passive content blocked"] =
|
||||
antitracking.hasMixedDisplayContentBlocked;
|
||||
details["tracking content blocked"] =
|
||||
antitracking.hasTrackingContentBlocked
|
||||
? `true (${antitracking.blockList})`
|
||||
: "false";
|
||||
|
||||
if (antitracking.hasTrackingContentBlocked) {
|
||||
extra_labels.push(
|
||||
`type-tracking-protection-${antitracking.blockList}`
|
||||
);
|
||||
}
|
||||
|
||||
for (const [framework, active] of Object.entries(frameworks)) {
|
||||
if (!active) {
|
||||
continue;
|
||||
}
|
||||
details[framework] = true;
|
||||
extra_labels.push(`type-${framework}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
#stripNonASCIIChars(str) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
return str.replace(/[^\x00-\x7F]/g, "");
|
||||
}
|
||||
|
||||
async receiveMessage(msg) {
|
||||
const { docShell } = this;
|
||||
switch (msg.name) {
|
||||
case "SendDataToWebcompatCom": {
|
||||
const win = docShell.domWindow;
|
||||
const expectedEndpoint = msg.data.endpointUrl;
|
||||
if (win.location.href == expectedEndpoint) {
|
||||
// Ensure that the tab has fully loaded and is waiting for messages
|
||||
const onLoad = () => {
|
||||
const payload = this.#formatReportDataForWebcompatCom(msg.data);
|
||||
const json = this.#stripNonASCIIChars(JSON.stringify(payload));
|
||||
const expectedOrigin = JSON.stringify(
|
||||
new URL(expectedEndpoint).origin
|
||||
);
|
||||
// webcompat.com checks that the message comes from its own origin
|
||||
const script = `
|
||||
const wrtReady = window.wrappedJSObject?.wrtReady;
|
||||
if (wrtReady) {
|
||||
console.info("Report Broken Site is waiting");
|
||||
}
|
||||
Promise.resolve(wrtReady).then(() => {
|
||||
console.debug(${json});
|
||||
postMessage(${json}, ${expectedOrigin})
|
||||
});`;
|
||||
RunScriptInFrame(win, script);
|
||||
};
|
||||
if (win.document.readyState == "complete") {
|
||||
onLoad();
|
||||
} else {
|
||||
win.addEventListener("load", onLoad, { once: true });
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case "GetWebCompatInfo": {
|
||||
return this.#getWebCompatInfo(docShell);
|
||||
}
|
||||
case "GetConsoleLog": {
|
||||
return this.#getLoggedMessages();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
/* vim: set ts=2 sw=2 et tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||
|
||||
class DriverInfo {
|
||||
constructor(gl, ext) {
|
||||
try {
|
||||
this.extensions = ext.getParameter(ext.EXTENSIONS);
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
this.renderer = ext.getParameter(gl.RENDERER);
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
this.vendor = ext.getParameter(gl.VENDOR);
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
this.version = ext.getParameter(gl.VERSION);
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
this.wsiInfo = ext.getParameter(ext.WSI_INFO);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
equals(info2) {
|
||||
return this.renderer == info2.renderer && this.version == info2.version;
|
||||
}
|
||||
|
||||
static getByType(driver) {
|
||||
const doc = new DOMParser().parseFromString("<html/>", "text/html");
|
||||
const canvas = doc.createElement("canvas");
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
let error;
|
||||
canvas.addEventListener("webglcontextcreationerror", function (e) {
|
||||
error = true;
|
||||
});
|
||||
let gl = null;
|
||||
try {
|
||||
gl = canvas.getContext(driver);
|
||||
} catch (e) {
|
||||
error = true;
|
||||
}
|
||||
if (error || !gl?.getExtension) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let ext = null;
|
||||
try {
|
||||
ext = gl.getExtension("MOZ_debug");
|
||||
} catch (e) {}
|
||||
if (!ext) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const data = new DriverInfo(gl, ext);
|
||||
|
||||
try {
|
||||
gl.getExtension("WEBGL_lose_context").loseContext();
|
||||
} catch (e) {}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static getAll() {
|
||||
const drivers = [];
|
||||
|
||||
function tryDriver(type) {
|
||||
const driver = DriverInfo.getByType(type);
|
||||
if (driver && !drivers.find(d => d.equals(driver))) {
|
||||
drivers.push(driver);
|
||||
}
|
||||
}
|
||||
|
||||
tryDriver("webgl");
|
||||
tryDriver("webgl2");
|
||||
tryDriver("webgpu");
|
||||
|
||||
return drivers;
|
||||
}
|
||||
}
|
||||
|
||||
export class ReportBrokenSiteParent extends JSWindowActorParent {
|
||||
#getAntitrackingBlockList() {
|
||||
// If content-track-digest256 is in the tracking table,
|
||||
// the user has enabled the strict list.
|
||||
const trackingTable = Services.prefs.getCharPref(
|
||||
"urlclassifier.trackingTable"
|
||||
);
|
||||
return trackingTable.includes("content") ? "strict" : "basic";
|
||||
}
|
||||
|
||||
#getAntitrackingInfo(browsingContext) {
|
||||
return {
|
||||
blockList: this.#getAntitrackingBlockList(),
|
||||
isPrivateBrowsing: browsingContext.usePrivateBrowsing,
|
||||
hasTrackingContentBlocked: !!(
|
||||
browsingContext.currentWindowGlobal.contentBlockingEvents &
|
||||
Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT
|
||||
),
|
||||
hasMixedActiveContentBlocked: !!(
|
||||
browsingContext.secureBrowserUI.state &
|
||||
Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT
|
||||
),
|
||||
hasMixedDisplayContentBlocked: !!(
|
||||
browsingContext.secureBrowserUI.state &
|
||||
Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
#getBasicGraphicsInfo(gfxInfo) {
|
||||
const get = name => {
|
||||
try {
|
||||
return gfxInfo[name];
|
||||
} catch (e) {}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const clean = rawObj => {
|
||||
const obj = JSON.parse(JSON.stringify(rawObj));
|
||||
if (!Object.keys(obj).length) {
|
||||
return undefined;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
const cleanDevice = (vendorID, deviceID, subsysID) => {
|
||||
return clean({ vendorID, deviceID, subsysID });
|
||||
};
|
||||
|
||||
const d1 = cleanDevice(
|
||||
get("adapterVendorID"),
|
||||
get("adapterDeviceID"),
|
||||
get("adapterSubsysID")
|
||||
);
|
||||
const d2 = cleanDevice(
|
||||
get("adapterVendorID2"),
|
||||
get("adapterDeviceID2"),
|
||||
get("adapterSubsysID2")
|
||||
);
|
||||
const devices = (get("isGPU2Active") ? [d2, d1] : [d1, d2]).filter(
|
||||
v => v !== undefined
|
||||
);
|
||||
|
||||
return clean({
|
||||
direct2DEnabled: get("direct2DEnabled"),
|
||||
directWriteEnabled: get("directWriteEnabled"),
|
||||
directWriteVersion: get("directWriteVersion"),
|
||||
hasTouchScreen: gfxInfo.getInfo().ApzTouchInput == 1,
|
||||
cleartypeParameters: get("clearTypeParameters"),
|
||||
targetFrameRate: get("targetFrameRate"),
|
||||
devices,
|
||||
});
|
||||
}
|
||||
|
||||
#getGraphicsInfo() {
|
||||
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
||||
|
||||
const data = this.#getBasicGraphicsInfo(gfxInfo);
|
||||
|
||||
const drivers = DriverInfo.getAll();
|
||||
if (drivers.length) {
|
||||
data.drivers = drivers.map(({ renderer, version }) => {
|
||||
return { renderer, version };
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const info = gfxInfo.CodecSupportInfo;
|
||||
if (info) {
|
||||
const codecs = {};
|
||||
for (const item of gfxInfo.CodecSupportInfo.split("\n")) {
|
||||
const [codec, type] = item.split(" ");
|
||||
if (!codecs[codec]) {
|
||||
codecs[codec] = { hw: false, sw: false };
|
||||
}
|
||||
if (type == "SW") {
|
||||
codecs[codec].software = true;
|
||||
} else if (type == "HW") {
|
||||
codecs[codec].hardware = true;
|
||||
}
|
||||
}
|
||||
data.codecSupport = codecs;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
const { features } = gfxInfo.getFeatureLog();
|
||||
data.features = {};
|
||||
for (let { name, log, status } of features) {
|
||||
for (const item of log.reverse()) {
|
||||
if (!item.failureId || item.status != status) {
|
||||
continue;
|
||||
}
|
||||
status = `${status} (${item.message || item.failureId})`;
|
||||
}
|
||||
data.features[name] = status;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
if (AppConstants.platform !== "android") {
|
||||
data.monitors = gfxInfo.getMonitors();
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async #getScreenshot(browsingContext, format, quality) {
|
||||
const zoom = browsingContext.fullZoom;
|
||||
const scale = browsingContext.topChromeWindow?.devicePixelRatio || 1;
|
||||
const wgp = browsingContext.currentWindowGlobal;
|
||||
|
||||
const image = await wgp.drawSnapshot(
|
||||
undefined, // rect
|
||||
scale * zoom,
|
||||
"white",
|
||||
undefined // resetScrollPosition
|
||||
);
|
||||
|
||||
const doc = Services.appShell.hiddenDOMWindow.document;
|
||||
const canvas = doc.createElement("canvas");
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
|
||||
const ctx = canvas.getContext("2d", { alpha: false });
|
||||
ctx.drawImage(image, 0, 0);
|
||||
image.close();
|
||||
|
||||
return canvas.toDataURL(`image/${format}`, quality / 100);
|
||||
}
|
||||
|
||||
async receiveMessage(msg) {
|
||||
switch (msg.name) {
|
||||
case "GetWebcompatInfoOnlyAvailableInParentProcess": {
|
||||
const { format, quality } = msg.data;
|
||||
const screenshot = await this.#getScreenshot(
|
||||
msg.target.browsingContext,
|
||||
format,
|
||||
quality
|
||||
).catch(e => {
|
||||
console.error("Report Broken Site: getting a screenshot failed", e);
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
return {
|
||||
antitracking: this.#getAntitrackingInfo(msg.target.browsingContext),
|
||||
graphics: this.#getGraphicsInfo(),
|
||||
locales: Services.locale.availableLocales,
|
||||
screenshot,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
FINAL_TARGET_FILES.actors = [
|
||||
"ReportBrokenSiteChild.sys.mjs",
|
||||
"ReportBrokenSiteParent.sys.mjs",
|
||||
]
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Web Compatibility", "Desktop")
|
|
@ -472,6 +472,23 @@ let JSWINDOWACTORS = {
|
|||
allFrames: true,
|
||||
},
|
||||
|
||||
ReportBrokenSite: {
|
||||
parent: {
|
||||
esModuleURI: "resource://gre/actors/ReportBrokenSiteParent.sys.mjs",
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "resource://gre/actors/ReportBrokenSiteChild.sys.mjs",
|
||||
},
|
||||
matches: [
|
||||
"http://*/*",
|
||||
"https://*/*",
|
||||
"about:certerror?*",
|
||||
"about:neterror?*",
|
||||
],
|
||||
messageManagerGroups: ["browsers"],
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
// This actor is available for all pages that one can
|
||||
// view the source of, however it won't be created until a
|
||||
// request to view the source is made via the message
|
||||
|
|
Загрузка…
Ссылка в новой задаче