Merge autoland to mozilla-central. a=merge

This commit is contained in:
Cosmin Sabou 2023-10-24 00:51:59 +03:00
commit 10d0e01455
380 changed files with 5397 additions and 7793 deletions

View file

@ -477,7 +477,6 @@ module.exports = {
"dom/base/test/unit_ipc/**",
"dom/base/test/jsmodules/**",
"dom/canvas/test/**",
"dom/encoding/test/**",
"dom/events/test/**",
"dom/file/ipc/tests/**",
"dom/file/tests/**",

View file

@ -59,7 +59,6 @@ browser/base/content/pageinfo/pageInfo.xhtml
browser/base/content/webext-panels.xhtml
browser/base/content/webrtcLegacyIndicator.xhtml
browser/components/downloads/content/contentAreaDownloadsView.xhtml
browser/components/migration/content/migration.xhtml
browser/components/places/content/bookmarkProperties.xhtml
browser/components/places/content/bookmarksSidebar.xhtml
browser/components/places/content/historySidebar.xhtml

View file

@ -1263,14 +1263,8 @@ already_AddRefed<AccAttributes> LocalAccessible::NativeAttributes() {
Atomize(eCSSProperty_text_align));
// Expose 'text-indent' attribute.
// XXX how does whatever reads this whether this was a percentage or a length?
const LengthPercentage& textIndent = f->StyleText()->mTextIndent;
if (textIndent.ConvertsToLength()) {
attributes->SetAttribute(nsGkAtoms::textIndent,
textIndent.ToLengthInCSSPixels());
} else if (textIndent.ConvertsToPercentage()) {
attributes->SetAttribute(nsGkAtoms::textIndent, textIndent.ToPercentage());
}
Atomize(eCSSProperty_text_indent));
auto GetMargin = [&](mozilla::Side aSide) -> CSSCoord {
// This is here only to guarantee that we do the same as getComputedStyle

View file

@ -699,12 +699,12 @@
<html:moz-button-group id="reset-pbm-panel-footer" class="panel-footer">
<button id="reset-pbm-panel-cancel-button"
class="panel-footer-button"
class="footer-button"
data-l10n-id="reset-pbm-panel-cancel-button"
oncommand="ResetPBMPanel.onCancel(this)"></button>
<button slot="primary"
id="reset-pbm-panel-confirm-button"
class="panel-footer-button"
class="footer-button"
data-l10n-id="reset-pbm-panel-confirm-button"
oncommand="ResetPBMPanel.onConfirm(this)"></button>
</html:moz-button-group>

View file

@ -288,31 +288,6 @@ function buildNotificationAction(msg, callback) {
}
var gXPInstallObserver = {
_findChildShell(aDocShell, aSoughtShell) {
if (aDocShell == aSoughtShell) {
return aDocShell;
}
var node = aDocShell.QueryInterface(Ci.nsIDocShellTreeItem);
for (var i = 0; i < node.childCount; ++i) {
var docShell = node.getChildAt(i);
docShell = this._findChildShell(docShell, aSoughtShell);
if (docShell == aSoughtShell) {
return docShell;
}
}
return null;
},
_getBrowser(aDocShell) {
for (let browser of gBrowser.browsers) {
if (this._findChildShell(browser.docShell, aDocShell)) {
return browser;
}
}
return null;
},
pendingInstalls: new WeakMap(),
showInstallConfirmation(browser, installInfo, height = undefined) {

View file

@ -2137,8 +2137,8 @@ var gProtectionsHandler = {
document.l10n.setAttributes(
toggle,
isPressed
? "protections-panel-etp-on-toggle"
: "protections-panel-etp-off-toggle",
? "protections-panel-etp-toggle-on"
: "protections-panel-etp-toggle-off",
{ host }
);
}

View file

@ -1148,15 +1148,6 @@ toolbarpaletteitem[dragover] {
flex-wrap: wrap;
}
#customization-toolbar-visibility-button > .box-inherit > .button-menu-dropmarker {
display: flex;
}
#customization-lwtheme-button > .box-inherit > .button-menu-dropmarker,
#customization-uidensity-button > .box-inherit > .button-menu-dropmarker {
display: flex;
}
toolbarpaletteitem[place="palette"] {
flex-direction: column;
width: 7em;

View file

@ -192,12 +192,12 @@
data-l10n-id="bookmark-panel"
data-l10n-attrs="style">
<button id="editBookmarkPanelDoneButton"
class="panel-footer-button"
class="footer-button"
data-l10n-id="bookmark-panel-save-button"
default="true"
oncommand="StarUI.panel.hidePopup();"/>
<button id="editBookmarkPanelRemoveButton"
class="panel-footer-button"
class="footer-button"
oncommand="StarUI.removeBookmarkButtonCommand();"/>
</html:moz-button-group>
</vbox>

View file

@ -73,11 +73,6 @@ var gExceptionPaths = [
// CSS files are referenced inside JS in an html template
"chrome://browser/content/aboutlogins/components/",
// These files are for the old migration wizard which will be removed
// shortly.
"chrome://browser/content/migration/migration.xhtml",
"chrome://browser/content/migration/migration.js",
];
// These are not part of the omni.ja file, so we find them only when running

View file

@ -3679,7 +3679,7 @@ BrowserGlue.prototype = {
_migrateUI() {
// Use an increasing number to keep track of the current migration state.
// Completely unrelated to the current Firefox release number.
const UI_VERSION = 141;
const UI_VERSION = 142;
const BROWSER_DOCURL = AppConstants.BROWSER_CHROME_URL;
if (!Services.prefs.prefHasUserValue("browser.migration.version")) {
@ -4268,6 +4268,23 @@ BrowserGlue.prototype = {
}
}
if (currentUIVersion < 142) {
// Bug 1860392 - Remove incorrectly persisted theming values from sidebar style.
try {
let value = xulStore.getValue(BROWSER_DOCURL, "sidebar-box", "style");
if (value) {
// Remove custom properties.
value = value
.split(";")
.filter(v => !v.trim().startsWith("--"))
.join(";");
xulStore.setValue(BROWSER_DOCURL, "sidebar-box", "style", value);
}
} catch (ex) {
console.error(ex);
}
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},

View file

@ -372,11 +372,11 @@
<html:moz-button-group id="protections-popup-sendReportView-footer"
class="panel-footer">
<button id="protections-popup-sendReportView-cancel"
class="panel-footer-button"
class="footer-button"
data-l10n-id="protections-panel-content-blocking-breakage-report-view-cancel"
oncommand="gProtectionsHandler._protectionsPopupMultiView.goBack();"/>
<button id="protections-popup-sendReportView-submit"
class="panel-footer-button"
class="footer-button"
default="true"
data-l10n-id="protections-panel-content-blocking-breakage-report-view-send-report"
oncommand="gProtectionsHandler.onSendReportClicked(); gProtectionsHandler.recordClick('send_report_submit');"/>
@ -399,17 +399,17 @@
<button id="protections-popup-cookieBannerView-cancel"
data-l10n-id="protections-panel-cookie-banner-view-cancel"
oncommand="gProtectionsHandler._protectionsPopupMultiView.goBack();"
class="panel-footer-button" />
class="footer-button" />
<button id="protections-popup-cookieBannerView-enable-button"
data-l10n-id="protections-panel-cookie-banner-view-turn-on"
oncommand="gProtectionsHandler.onCookieBannerToggleCommand()"
default="true"
class="panel-footer-button" />
class="footer-button" />
<button id="protections-popup-cookieBannerView-disable-button"
data-l10n-id="protections-panel-cookie-banner-view-turn-off"
oncommand="gProtectionsHandler.onCookieBannerToggleCommand()"
default="true"
class="panel-footer-button" />
class="footer-button" />
</html:moz-button-group>
</panelview>
</panelmultiview>

View file

@ -29,12 +29,12 @@
# NB: because oncommand fires after click, by the time we've fired, the checkbox binding
# will already have switched the button's state, so this is correct:
oncommand="gCustomizeMode.toggleTitlebar(this.checked)" data-l10n-id="customize-mode-titlebar"/>
<button id="customization-toolbar-visibility-button" class="customizationmode-button" type="menu" data-l10n-id="customize-mode-toolbars">
<button id="customization-toolbar-visibility-button" class="footer-button" type="menu" data-l10n-id="customize-mode-toolbars">
<menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
</button>
<button id="customization-uidensity-button"
data-l10n-id="customize-mode-uidensity"
class="customizationmode-button"
class="footer-button"
type="menu"
hidden="true">
<panel type="arrow" id="customization-uidensity-menu"
@ -90,30 +90,31 @@
<button id="whimsy-button"
type="checkbox"
class="customizationmode-button"
class="footer-button"
oncommand="gCustomizeMode.togglePong(this.checked);"
hidden="true"/>
<spacer id="customization-footer-spacer"/>
#ifdef XP_MACOSX
<button id="customization-touchbar-button"
class="customizationmode-button"
class="footer-button"
hidden="true"
oncommand="gCustomizeMode.customizeTouchBar();"
data-l10n-id="customize-mode-touchbar-cmd"/>
<spacer hidden="true" id="customization-touchbar-spacer"/>
#endif
<button id="customization-undo-reset-button"
class="customizationmode-button"
class="footer-button"
hidden="true"
oncommand="gCustomizeMode.undoReset();"
data-l10n-id="customize-mode-undo-cmd"/>
<button id="customization-reset-button"
oncommand="gCustomizeMode.reset();"
data-l10n-id="customize-mode-restore-defaults"
class="customizationmode-button"/>
class="footer-button"/>
<button id="customization-done-button"
oncommand="gCustomizeMode.exit();"
data-l10n-id="customize-mode-done"
class="customizationmode-button"/>
default="true"
class="footer-button"/>
</hbox>

View file

@ -182,10 +182,10 @@
<html:moz-button-group id="downloadsPanel-blockedSubview-buttons"
class="panel-footer">
<button id="downloadsPanel-blockedSubview-unblockButton"
class="panel-footer-button"
class="footer-button"
command="downloadsCmd_unblockAndOpen"/>
<button id="downloadsPanel-blockedSubview-deleteButton"
class="panel-footer-button"
class="footer-button"
oncommand="DownloadsBlockedSubview.confirmBlock();"
default="true"/>
</html:moz-button-group>

View file

@ -277,6 +277,7 @@ fail-if = ["a11y_checks"] # Bug 1854460 clicked menuitem(s) may not be accessibl
["browser_ext_menus_incognito.js"]
["browser_ext_menus_refresh.js"]
skip-if = ["a11y_checks"] # Bug 1858041 and 1835079 for causing intermittent crashes
["browser_ext_menus_replace_menu.js"]

View file

@ -675,8 +675,6 @@ class MigrationUtils {
* source-selection page will be displayed, either with the default
* browser selected, if it could be detected and if there is a
* migrator for it, or with the first option selected as a fallback
* (The first option is hardcoded to be the most common browser for
* the OS we run on. See migration.xhtml).
* @param {string|null} [aProfileToMigrate=null]
* If set, the migration wizard will import from the profile indicated.
* @throws
@ -1071,12 +1069,12 @@ class MigrationUtils {
* Enum for the entrypoint that is being used to start migration.
* Callers can use the MIGRATION_ENTRYPOINTS getter to use these.
*
* These values are what's written into the FX_MIGRATION_ENTRY_POINT
* histogram after a migration.
* These values are what's written into the
* FX_MIGRATION_ENTRY_POINT_CATEGORICAL histogram after a migration.
*
* @see MIGRATION_ENTRYPOINTS
* @readonly
* @enum {number}
* @enum {string}
*/
#MIGRATION_ENTRYPOINTS_ENUM = Object.freeze({
/** The entrypoint was not supplied */
@ -1124,54 +1122,8 @@ class MigrationUtils {
}
/**
* Translates an entrypoint string into the proper numeric value for the legacy
* FX_MIGRATION_ENTRY_POINT histogram.
*
* @param {string} entrypoint
* The entrypoint to translate from MIGRATION_ENTRYPOINTS.
* @returns {number}
* The numeric value for the legacy FX_MIGRATION_ENTRY_POINT histogram.
*/
getLegacyMigrationEntrypoint(entrypoint) {
switch (entrypoint) {
case this.MIGRATION_ENTRYPOINTS.FIRSTRUN: {
return 1;
}
case this.MIGRATION_ENTRYPOINTS.FXREFRESH: {
return 2;
}
case this.MIGRATION_ENTRYPOINTS.PLACES: {
return 3;
}
case this.MIGRATION_ENTRYPOINTS.PASSWORDS: {
return 4;
}
case this.MIGRATION_ENTRYPOINTS.NEWTAB: {
return 5;
}
case this.MIGRATION_ENTRYPOINTS.FILE_MENU: {
return 6;
}
case this.MIGRATION_ENTRYPOINTS.HELP_MENU: {
return 7;
}
case this.MIGRATION_ENTRYPOINTS.BOOKMARKS_TOOLBAR: {
return 8;
}
case this.MIGRATION_ENTRYPOINTS.PREFERENCES: {
return 9;
}
case this.MIGRATION_ENTRYPOINTS.UNKNOWN:
// Intentional fall-through
default: {
return 0; // Unknown
}
}
}
/**
* Enum for the numeric value written to the FX_MIGRATION_SOURCE_BROWSER,
* and FX_STARTUP_MIGRATION_EXISTING_DEFAULT_BROWSER histograms.
* Enum for the numeric value written to the FX_MIGRATION_SOURCE_BROWSER.
* histogram
*
* @see getSourceIdForTelemetry
* @readonly

View file

@ -1,832 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const { MigrationUtils } = ChromeUtils.importESModule(
"resource:///modules/MigrationUtils.sys.mjs"
);
const { MigratorBase } = ChromeUtils.importESModule(
"resource:///modules/MigratorBase.sys.mjs"
);
/**
* Map from data types that match Ci.nsIBrowserProfileMigrator's types to
* prefixes for strings used to label these data types in the migration
* dialog. We use these strings with -checkbox and -label suffixes for the
* checkboxes on the "importItems" page, and for the labels on the "migrating"
* and "done" pages, respectively.
*/
const kDataToStringMap = new Map([
["cookies", "browser-data-cookies"],
["history", "browser-data-history"],
["formdata", "browser-data-formdata"],
["passwords", "browser-data-passwords"],
["bookmarks", "browser-data-bookmarks"],
["otherdata", "browser-data-otherdata"],
["session", "browser-data-session"],
["payment_methods", "browser-data-payment-methods"],
]);
var MigrationWizard = {
/* exported MigrationWizard */
_source: "", // Source Profile Migrator ContractID suffix
_itemsFlags: MigrationUtils.resourceTypes.ALL, // Selected Import Data Sources (16-bit bitfield)
_selectedProfile: null, // Selected Profile name to import from
_wiz: null,
_migrator: null,
_autoMigrate: null,
_receivedPermissions: new Set(),
_succeededMigrationEventArgs: null,
_openedTime: null,
init() {
Services.telemetry.setEventRecordingEnabled("browser.migration", true);
let os = Services.obs;
os.addObserver(this, "Migration:Started");
os.addObserver(this, "Migration:ItemBeforeMigrate");
os.addObserver(this, "Migration:ItemAfterMigrate");
os.addObserver(this, "Migration:ItemError");
os.addObserver(this, "Migration:Ended");
this._wiz = document.querySelector("wizard");
let args = window.arguments[0]?.wrappedJSObject || {};
let entrypoint =
args.entrypoint || MigrationUtils.MIGRATION_ENTRYPOINTS.UNKNOWN;
Services.telemetry
.getHistogramById("FX_MIGRATION_ENTRY_POINT_CATEGORICAL")
.add(entrypoint);
// The legacy entrypoint Histogram wasn't categorical, so we translate to the right
// numeric value before writing it. We'll keep this Histogram around to ensure a
// smooth transition to the new FX_MIGRATION_ENTRY_POINT_CATEGORICAL categorical
// histogram.
let entryPointId = MigrationUtils.getLegacyMigrationEntrypoint(entrypoint);
Services.telemetry
.getHistogramById("FX_MIGRATION_ENTRY_POINT")
.add(entryPointId);
// If the caller passed openedTime, that means this is the first time that
// the migration wizard is opening, and we want to measure its performance.
// Stash the time that opening was invoked so that we can measure the
// total elapsed time when the source list is shown.
if (args.openedTime) {
this._openedTime = args.openedTime;
}
this.isInitialMigration =
entrypoint == MigrationUtils.MIGRATION_ENTRYPOINTS.FIRSTRUN;
// Record that the uninstaller requested a profile refresh
if (Services.env.get("MOZ_UNINSTALLER_PROFILE_REFRESH")) {
Services.env.set("MOZ_UNINSTALLER_PROFILE_REFRESH", "");
Services.telemetry.scalarSet(
"migration.uninstaller_profile_refresh",
true
);
}
this._source = args.migratorKey;
this._migrator =
args.migrator instanceof MigratorBase ? args.migrator : null;
this._autoMigrate = !!args.isStartupMigration;
this._skipImportSourcePage = !!args.skipSourceSelection;
if (this._migrator && args.profileId) {
let sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
this._selectedProfile = sourceProfiles.find(
profile => profile.id == args.profileId
);
}
if (this._autoMigrate) {
// Show the "nothing" option in the automigrate case to provide an
// easily identifiable way to avoid migration and create a new profile.
document.getElementById("nothing").hidden = false;
}
this._setSourceForDataLocalization();
document.addEventListener("wizardcancel", function () {
MigrationWizard.onWizardCancel();
});
document
.getElementById("selectProfile")
.addEventListener("pageshow", function () {
MigrationWizard.onSelectProfilePageShow();
});
document
.getElementById("importItems")
.addEventListener("pageshow", function () {
MigrationWizard.onImportItemsPageShow();
});
document
.getElementById("migrating")
.addEventListener("pageshow", function () {
MigrationWizard.onMigratingPageShow();
});
document.getElementById("done").addEventListener("pageshow", function () {
MigrationWizard.onDonePageShow();
});
document
.getElementById("selectProfile")
.addEventListener("pagerewound", function () {
MigrationWizard.onSelectProfilePageRewound();
});
document
.getElementById("importItems")
.addEventListener("pagerewound", function () {
MigrationWizard.onImportItemsPageRewound();
});
document
.getElementById("selectProfile")
.addEventListener("pageadvanced", function () {
MigrationWizard.onSelectProfilePageAdvanced();
});
document
.getElementById("importItems")
.addEventListener("pageadvanced", function () {
MigrationWizard.onImportItemsPageAdvanced();
});
document
.getElementById("importPermissions")
.addEventListener("pageadvanced", function (e) {
MigrationWizard.onImportPermissionsPageAdvanced(e);
});
document
.getElementById("importSource")
.addEventListener("pageadvanced", function (e) {
MigrationWizard.onImportSourcePageAdvanced(e);
});
this.recordEvent("opened");
this.onImportSourcePageShow();
},
uninit() {
var os = Services.obs;
os.removeObserver(this, "Migration:Started");
os.removeObserver(this, "Migration:ItemBeforeMigrate");
os.removeObserver(this, "Migration:ItemAfterMigrate");
os.removeObserver(this, "Migration:ItemError");
os.removeObserver(this, "Migration:Ended");
os.notifyObservers(this, "MigrationWizard:Destroyed");
MigrationUtils.finishMigration();
},
/**
* Used for recording telemetry in the migration wizard.
*
* @param {string} type
* The type of event being recorded.
* @param {object} args
* The data to pass to telemetry when the event is recorded.
*/
recordEvent(type, args = null) {
Services.telemetry.recordEvent(
"browser.migration",
type,
"legacy_wizard",
null,
args
);
},
spinResolve(promise) {
let canAdvance = this._wiz.canAdvance;
let canRewind = this._wiz.canRewind;
this._wiz.canAdvance = false;
this._wiz.canRewind = false;
let result = MigrationUtils.spinResolve(promise);
this._wiz.canAdvance = canAdvance;
this._wiz.canRewind = canRewind;
return result;
},
_setSourceForDataLocalization() {
this._sourceForDataLocalization = this._source;
// Ensure consistency for various channels, brandings and versions of
// Chromium and MS Edge.
if (this._sourceForDataLocalization) {
this._sourceForDataLocalization = this._sourceForDataLocalization
.replace(/^(chromium-edge-beta|chromium-edge)$/, "edge")
.replace(/^(canary|chromium|chrome-beta|chrome-dev)$/, "chrome");
}
},
onWizardCancel() {
MigrationUtils.forceExitSpinResolve();
return true;
},
// 1 - Import Source
onImportSourcePageShow() {
this._wiz.canRewind = false;
var selectedMigrator = null;
this._availableMigrators = [];
// Figure out what source apps are are available to import from:
var group = document.getElementById("importSourceGroup");
for (var i = 0; i < group.childNodes.length; ++i) {
var migratorKey = group.childNodes[i].id;
if (migratorKey != "nothing") {
var migrator = this.spinResolve(
MigrationUtils.getMigrator(migratorKey)
);
if (migrator?.enabled) {
// Save this as the first selectable item, if we don't already have
// one, or if it is the migrator that was passed to us.
if (!selectedMigrator || this._source == migratorKey) {
selectedMigrator = group.childNodes[i];
}
let profiles = this.spinResolve(migrator.getSourceProfiles());
if (profiles?.length) {
Services.telemetry.keyedScalarAdd(
"migration.discovered_migrators",
migratorKey,
profiles.length
);
} else {
Services.telemetry.keyedScalarAdd(
"migration.discovered_migrators",
migratorKey,
1
);
}
this._availableMigrators.push([migratorKey, migrator]);
} else {
// Hide this option
group.childNodes[i].hidden = true;
}
}
}
if (this.isInitialMigration) {
Services.telemetry
.getHistogramById("FX_STARTUP_MIGRATION_BROWSER_COUNT")
.add(this._availableMigrators.length);
let defaultBrowser = MigrationUtils.getMigratorKeyForDefaultBrowser();
// This will record 0 for unknown default browser IDs.
defaultBrowser = MigrationUtils.getSourceIdForTelemetry(defaultBrowser);
Services.telemetry
.getHistogramById("FX_STARTUP_MIGRATION_EXISTING_DEFAULT_BROWSER")
.add(defaultBrowser);
}
if (selectedMigrator) {
group.selectedItem = selectedMigrator;
} else {
this.recordEvent("no_browsers_found");
// We didn't find a migrator, notify the user
document.getElementById("noSources").hidden = false;
this._wiz.canAdvance = false;
document.getElementById("importAll").hidden = true;
}
// This must be the first time we're opening the migration wizard,
// and we want to know how long it took to get to this point, where
// we're showing the source list.
if (this._openedTime !== null) {
let elapsed = Cu.now() - this._openedTime;
Services.telemetry.scalarSet(
"migration.time_to_produce_legacy_migrator_list",
elapsed
);
}
// Advance to the next page if the caller told us to.
if (this._migrator && this._skipImportSourcePage) {
this._wiz.advance();
this._wiz.canRewind = false;
}
},
onImportSourcePageAdvanced(event) {
var newSource =
document.getElementById("importSourceGroup").selectedItem.id;
this.recordEvent("browser_selected", { migrator_key: newSource });
if (newSource == "nothing") {
// Need to do telemetry here because we're closing the dialog before we get to
// do actual migration. For actual migration, this doesn't happen until after
// migration takes place.
Services.telemetry
.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
.add(MigrationUtils.getSourceIdForTelemetry("nothing"));
this._wiz.cancel();
event.preventDefault();
}
if (!this._migrator || newSource != this._source) {
// Create the migrator for the selected source.
this._migrator = this.spinResolve(MigrationUtils.getMigrator(newSource));
this._itemsFlags = MigrationUtils.resourceTypes.ALL;
this._selectedProfile = null;
}
this._source = newSource;
this._setSourceForDataLocalization();
// check for more than one source profile
var sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
if (this._skipImportSourcePage) {
this._updateNextPageForPermissions();
} else if (sourceProfiles && sourceProfiles.length > 1) {
this._wiz.currentPage.next = "selectProfile";
} else {
if (this._autoMigrate) {
this._updateNextPageForPermissions();
} else {
this._wiz.currentPage.next = "importItems";
}
if (sourceProfiles && sourceProfiles.length == 1) {
this._selectedProfile = sourceProfiles[0];
} else {
this._selectedProfile = null;
}
}
},
// 2 - [Profile Selection]
onSelectProfilePageShow() {
// Disabling this for now, since we ask about import sources in automigration
// too and don't want to disable the back button
// if (this._autoMigrate)
// document.documentElement.getButton("back").disabled = true;
var profiles = document.getElementById("profiles");
while (profiles.hasChildNodes()) {
profiles.firstChild.remove();
}
// Note that this block is still reached even if the user chose 'From File'
// and we canceled the dialog. When that happens, _migrator will be null.
if (this._migrator) {
var sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
for (let profile of sourceProfiles) {
var item = document.createXULElement("radio");
item.id = profile.id;
item.setAttribute("label", profile.name);
profiles.appendChild(item);
}
}
profiles.selectedItem = this._selectedProfile
? document.getElementById(this._selectedProfile.id)
: profiles.firstChild;
},
onSelectProfilePageRewound() {
var profiles = document.getElementById("profiles");
let sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
this._selectedProfile =
sourceProfiles.find(profile => profile.id == profiles.selectedItem.id) ||
null;
},
onSelectProfilePageAdvanced() {
this.recordEvent("profile_selected", {
migrator_key: this._source,
});
var profiles = document.getElementById("profiles");
let sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
this._selectedProfile =
sourceProfiles.find(profile => profile.id == profiles.selectedItem.id) ||
null;
// If we're automigrating or just doing bookmarks don't show the item selection page
if (this._autoMigrate) {
this._updateNextPageForPermissions();
}
},
// 3 - ImportItems
onImportItemsPageShow() {
var dataSources = document.getElementById("dataSources");
while (dataSources.hasChildNodes()) {
dataSources.firstChild.remove();
}
var items = this.spinResolve(
this._migrator.getMigrateData(this._selectedProfile)
);
for (let itemType of kDataToStringMap.keys()) {
let itemValue = MigrationUtils.resourceTypes[itemType.toUpperCase()];
if (items & itemValue) {
let checkbox = document.createXULElement("checkbox");
checkbox.id = itemValue;
checkbox.setAttribute("native", true);
document.l10n.setAttributes(
checkbox,
kDataToStringMap.get(itemType) + "-checkbox",
{ browser: this._sourceForDataLocalization }
);
dataSources.appendChild(checkbox);
if (!this._itemsFlags || this._itemsFlags & itemValue) {
checkbox.checked = true;
}
}
}
},
onImportItemsPageRewound() {
this._wiz.canAdvance = true;
this.onImportItemsPageAdvanced(true /* viaRewind */);
},
onImportItemsPageAdvanced(viaRewind = false) {
let extraKeys = {
migrator_key: this._source,
history: "0",
formdata: "0",
passwords: "0",
bookmarks: "0",
payment_methods: "0",
// "other" will get incremented, so we keep this as a number for
// now, and will cast to a string before submitting to Event telemetry.
other: 0,
configured: "0",
};
var dataSources = document.getElementById("dataSources");
this._itemsFlags = 0;
for (var i = 0; i < dataSources.childNodes.length; ++i) {
var checkbox = dataSources.childNodes[i];
if (checkbox.localName == "checkbox" && checkbox.checked) {
let flag = parseInt(checkbox.id);
switch (flag) {
case MigrationUtils.resourceTypes.HISTORY:
extraKeys.history = "1";
break;
case MigrationUtils.resourceTypes.FORMDATA:
extraKeys.formdata = "1";
break;
case MigrationUtils.resourceTypes.PASSWORDS:
extraKeys.passwords = "1";
break;
case MigrationUtils.resourceTypes.BOOKMARKS:
extraKeys.bookmarks = "1";
break;
case MigrationUtils.resourceTypes.PAYMENT_METHODS:
extraKeys.payment_methods = "1";
break;
default:
extraKeys.other++;
}
this._itemsFlags |= parseInt(checkbox.id);
}
}
extraKeys.other = String(extraKeys.other);
if (!viaRewind) {
this.recordEvent("resources_selected", extraKeys);
}
this._updateNextPageForPermissions();
},
onImportItemCommand() {
var items = document.getElementById("dataSources");
var checkboxes = items.getElementsByTagName("checkbox");
var oneChecked = false;
for (var i = 0; i < checkboxes.length; ++i) {
if (checkboxes[i].checked) {
oneChecked = true;
break;
}
}
this._wiz.canAdvance = oneChecked;
this._updateNextPageForPermissions();
},
_updateNextPageForPermissions() {
// We would like to just go straight to work:
this._wiz.currentPage.next = "migrating";
// If we already have permissions, this is easy:
if (this._receivedPermissions.has(this._source)) {
return;
}
// Otherwise, if we're on mojave or later and importing from
// Safari, prompt for the bookmarks file.
// We may add other browser/OS combos here in future.
if (
this._source == "safari" &&
AppConstants.isPlatformAndVersionAtLeast("macosx", "18") &&
(this._itemsFlags & MigrationUtils.resourceTypes.BOOKMARKS ||
this._itemsFlags == MigrationUtils.resourceTypes.ALL)
) {
let havePermissions = this.spinResolve(this._migrator.hasPermissions());
if (!havePermissions) {
this._wiz.currentPage.next = "importPermissions";
this.recordEvent("safari_perms");
}
}
},
// 3b: permissions. This gets invoked when the user clicks "Next"
async onImportPermissionsPageAdvanced(event) {
// We're done if we have permission:
if (this._receivedPermissions.has(this._source)) {
return;
}
// The wizard helper is sync, and we need to check some stuff, so just stop
// advancing for now and prompt the user, then advance the wizard if everything
// worked.
event.preventDefault();
await this._migrator.getPermissions(window);
if (await this._migrator.hasPermissions()) {
this._receivedPermissions.add(this._source);
// Re-enter (we'll then allow the advancement through the early return above)
this._wiz.advance();
}
// if we didn't have permissions after the `getPermissions` call, the user
// cancelled the dialog. Just no-op out now; the user can re-try by clicking
// the 'Continue' button again, or go back and pick a different browser.
},
// 4 - Migrating
onMigratingPageShow() {
this._wiz.getButton("cancel").disabled = true;
this._wiz.canRewind = false;
this._wiz.canAdvance = false;
// When automigrating, show all of the data that can be received from this source.
if (this._autoMigrate) {
this._itemsFlags = this.spinResolve(
this._migrator.getMigrateData(this._selectedProfile)
);
}
this._listItems("migratingItems");
setTimeout(() => this.onMigratingMigrate(), 0);
},
async onMigratingMigrate() {
await this._migrator.migrate(
this._itemsFlags,
this._autoMigrate,
this._selectedProfile
);
Services.telemetry
.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
.add(MigrationUtils.getSourceIdForTelemetry(this._source));
if (!this._autoMigrate) {
let hist = Services.telemetry.getKeyedHistogramById("FX_MIGRATION_USAGE");
let exp = 0;
let items = this._itemsFlags;
while (items) {
if (items & 1) {
hist.add(this._source, exp);
}
items = items >> 1;
exp++;
}
}
},
_listItems(aID) {
var items = document.getElementById(aID);
while (items.hasChildNodes()) {
items.firstChild.remove();
}
for (let itemType of kDataToStringMap.keys()) {
let itemValue = MigrationUtils.resourceTypes[itemType.toUpperCase()];
if (this._itemsFlags & itemValue) {
var label = document.createXULElement("label");
label.id = itemValue + "_migrated";
try {
document.l10n.setAttributes(
label,
kDataToStringMap.get(itemType) + "-label",
{ browser: this._sourceForDataLocalization }
);
items.appendChild(label);
} catch (e) {
// if the block above throws, we've enumerated all the import data types we
// currently support and are now just wasting time, break.
break;
}
}
}
},
recordResourceMigration(obj, resourceType) {
// Sometimes, the resourceType that gets passed here is a string, which
// is bizarre. We'll hold our nose and accept either a string or a
// number.
resourceType = parseInt(resourceType, 10);
switch (resourceType) {
case MigrationUtils.resourceTypes.HISTORY:
obj.history = "1";
break;
case MigrationUtils.resourceTypes.FORMDATA:
obj.formdata = "1";
break;
case MigrationUtils.resourceTypes.PASSWORDS:
obj.passwords = "1";
break;
case MigrationUtils.resourceTypes.BOOKMARKS:
obj.bookmarks = "1";
break;
case MigrationUtils.resourceTypes.PAYMENT_METHODS:
obj.payment_methods = "1";
break;
default:
obj.other++;
}
},
recordMigrationStartEvent(resourceFlags) {
let extraKeys = {
migrator_key: this._source,
history: "0",
formdata: "0",
passwords: "0",
bookmarks: "0",
payment_methods: "0",
// "other" will get incremented, so we keep this as a number for
// now, and will cast to a string before submitting to Event telemetry.
other: 0,
};
for (let resourceTypeKey in MigrationUtils.resourceTypes) {
let resourceType = MigrationUtils.resourceTypes[resourceTypeKey];
if (resourceFlags & resourceType) {
this.recordResourceMigration(extraKeys, resourceType);
}
}
extraKeys.other = String(extraKeys.other);
this.recordEvent("migration_started", extraKeys);
},
observe(aSubject, aTopic, aData) {
var label;
switch (aTopic) {
case "Migration:Started":
this._succeededMigrationEventArgs = {
migrator_key: this._source,
history: "0",
formdata: "0",
passwords: "0",
bookmarks: "0",
payment_methods: "0",
// "other" will get incremented, so we keep this as a number for
// now, and will cast to a string before submitting to Event telemetry.
other: 0,
};
this.recordMigrationStartEvent(this._itemsFlags);
break;
case "Migration:ItemBeforeMigrate":
label = document.getElementById(aData + "_migrated");
if (label) {
label.setAttribute("style", "font-weight: bold");
}
break;
case "Migration:ItemAfterMigrate":
this.recordResourceMigration(this._succeededMigrationEventArgs, aData);
label = document.getElementById(aData + "_migrated");
if (label) {
label.removeAttribute("style");
}
break;
case "Migration:Ended":
this._succeededMigrationEventArgs.other = String(
this._succeededMigrationEventArgs.other
);
this.recordEvent(
"migration_finished",
this._succeededMigrationEventArgs
);
if (this.isInitialMigration) {
// Ensure errors in reporting data recency do not affect the rest of the migration.
try {
this.reportDataRecencyTelemetry();
} catch (ex) {
console.error(ex);
}
}
if (this._autoMigrate) {
// We're done now.
this._wiz.canAdvance = true;
this._wiz.advance();
setTimeout(close, 5000);
} else {
this._wiz.canAdvance = true;
var nextButton = this._wiz.getButton("next");
nextButton.click();
}
break;
case "Migration:ItemError":
let type = "undefined";
let numericType = parseInt(aData);
switch (numericType) {
case MigrationUtils.resourceTypes.COOKIES:
type = "cookies";
break;
case MigrationUtils.resourceTypes.HISTORY:
type = "history";
break;
case MigrationUtils.resourceTypes.FORMDATA:
type = "form data";
break;
case MigrationUtils.resourceTypes.PASSWORDS:
type = "passwords";
break;
case MigrationUtils.resourceTypes.BOOKMARKS:
type = "bookmarks";
break;
case MigrationUtils.resourceTypes.PAYMENT_METHODS:
type = "payment methods";
break;
case MigrationUtils.resourceTypes.OTHERDATA:
type = "misc. data";
break;
}
Services.console.logStringMessage(
"some " + type + " did not successfully migrate."
);
Services.telemetry
.getKeyedHistogramById("FX_MIGRATION_ERRORS")
.add(this._source, Math.log2(numericType));
break;
}
},
onDonePageShow() {
this._wiz.getButton("cancel").disabled = true;
this._wiz.canRewind = false;
this._listItems("doneItems");
},
reportDataRecencyTelemetry() {
let histogram = Services.telemetry.getKeyedHistogramById(
"FX_STARTUP_MIGRATION_DATA_RECENCY"
);
let lastUsedPromises = [];
for (let [key, migrator] of this._availableMigrators) {
// No block-scoped let in for...of loop conditions, so get the source:
let localKey = key;
lastUsedPromises.push(
migrator.getLastUsedDate().then(date => {
const ONE_YEAR = 24 * 365;
let diffInHours = Math.round((Date.now() - date) / (60 * 60 * 1000));
if (diffInHours > ONE_YEAR) {
diffInHours = ONE_YEAR;
}
histogram.add(localKey, diffInHours);
return [localKey, diffInHours];
})
);
}
Promise.all(lastUsedPromises).then(migratorUsedTimeDiff => {
// Sort low to high.
migratorUsedTimeDiff.sort(
([keyA, diffA], [keyB, diffB]) => diffA - diffB
); /* eslint no-unused-vars: off */
let usedMostRecentBrowser =
migratorUsedTimeDiff.length &&
this._source == migratorUsedTimeDiff[0][0];
let usedRecentBrowser = Services.telemetry.getKeyedHistogramById(
"FX_STARTUP_MIGRATION_USED_RECENT_BROWSER"
);
usedRecentBrowser.add(this._source, usedMostRecentBrowser);
});
},
};

View file

@ -1,113 +0,0 @@
<?xml version="1.0"?>
# 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/.
<window id="migrationWizard"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="migration-wizard"
windowtype="Browser:MigrationWizard"
onload="MigrationWizard.init()"
onunload="MigrationWizard.uninit()"
style="min-width: 40em;"
buttons="accept,cancel">
<linkset>
<html:link rel="stylesheet" href="chrome://global/skin/global.css" />
<html:link rel="localization" href="branding/brand.ftl"/>
<html:link rel="localization" href="toolkit/global/wizard.ftl"/>
<html:link rel="localization" href="browser/migration.ftl"/>
</linkset>
<script src="chrome://global/content/customElements.js"/>
<script src="chrome://browser/content/migration/migration.js"/>
<wizard data-branded="true">
<wizardpage id="importSource" pageid="importSource" next="selectProfile"
data-header-label-id="import-source-page-title">
<description id="importAll" control="importSourceGroup" data-l10n-id="import-from"></description>
<description id="importBookmarks" control="importSourceGroup" data-l10n-id="import-from-bookmarks" hidden="true" ></description>
<radiogroup id="importSourceGroup" align="start">
# NB: if you add items to this list, please also assign them a unique migrator ID in MigrationUtils.jsm
<radio id="firefox" data-l10n-id="import-from-firefox"/>
#ifdef XP_WIN
<radio id="chromium-edge" data-l10n-id="import-from-edge"/>
<radio id="edge" data-l10n-id="import-from-edge-legacy" />
<radio id="chromium-edge-beta" data-l10n-id="import-from-edge-beta"/>
<radio id="ie" data-l10n-id="import-from-ie"/>
<radio id="opera" data-l10n-id="import-from-opera"/>
<radio id="brave" data-l10n-id="import-from-brave"/>
<radio id="chrome" data-l10n-id="import-from-chrome"/>
<radio id="chrome-beta" data-l10n-id="import-from-chrome-beta"/>
<radio id="chromium" data-l10n-id="import-from-chromium"/>
<radio id="canary" data-l10n-id="import-from-canary" />
<radio id="vivaldi" data-l10n-id="import-from-vivaldi"/>
<radio id="chromium-360se" data-l10n-id="import-from-360se"/>
<radio id="opera-gx" data-l10n-id="import-from-opera-gx"/>
#elifdef XP_MACOSX
<radio id="safari" data-l10n-id="import-from-safari"/>
<radio id="opera" data-l10n-id="import-from-opera"/>
<radio id="brave" data-l10n-id="import-from-brave"/>
<radio id="chrome" data-l10n-id="import-from-chrome"/>
<radio id="chromium-edge" data-l10n-id="import-from-edge"/>
<radio id="chromium-edge-beta" data-l10n-id="import-from-edge-beta"/>
<radio id="chromium" data-l10n-id="import-from-chromium"/>
<radio id="canary" data-l10n-id="import-from-canary"/>
<radio id="vivaldi" data-l10n-id="import-from-vivaldi"/>
<radio id="opera-gx" data-l10n-id="import-from-opera-gx"/>
#elifdef XP_UNIX
<radio id="opera" data-l10n-id="import-from-opera"/>
<radio id="vivaldi" data-l10n-id="import-from-vivaldi"/>
<radio id="brave" data-l10n-id="import-from-brave"/>
<radio id="chrome" data-l10n-id="import-from-chrome"/>
<radio id="chrome-beta" data-l10n-id="import-from-chrome-beta"/>
<radio id="chrome-dev" data-l10n-id="import-from-chrome-dev"/>
<radio id="chromium" data-l10n-id="import-from-chromium"/>
#endif
<radio id="nothing" data-l10n-id="import-from-nothing" hidden="true"/>
</radiogroup>
<label id="noSources" hidden="true" data-l10n-id="no-migration-sources"></label>
</wizardpage>
<wizardpage id="selectProfile" pageid="selectProfile"
data-header-label-id="import-select-profile-page-title"
next="importItems">
<description control="profiles" data-l10n-id="import-select-profile-description"></description>
<radiogroup id="profiles" align="start"/>
</wizardpage>
<wizardpage id="importItems" pageid="importItems"
data-header-label-id="import-items-page-title"
next="migrating"
oncommand="MigrationWizard.onImportItemCommand();">
<description control="dataSources" data-l10n-id="import-items-description"></description>
<vbox id="dataSources" style="overflow: auto; appearance: auto; -moz-default-appearance: listbox" align="start" flex="1" role="group"/>
</wizardpage>
<wizardpage id="importPermissions" pageid="importPermissions"
data-header-label-id="import-permissions-page-title"
next="migrating">
<description data-l10n-id="import-safari-permissions-string"></description>
</wizardpage>
<wizardpage id="migrating" pageid="migrating"
data-header-label-id="import-migrating-page-title"
next="done">
<description control="migratingItems" data-l10n-id="import-migrating-description"></description>
<vbox id="migratingItems" style="overflow: auto;" align="start" role="group"/>
</wizardpage>
<wizardpage id="done" pageid="done"
data-header-label-id="import-done-page-title">
<description control="doneItems" data-l10n-id="import-done-description"></description>
<vbox id="doneItems" style="overflow: auto;" align="start" role="group"/>
</wizardpage>
</wizard>
</window>

View file

@ -3,8 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
browser.jar:
* content/browser/migration/migration.xhtml (content/migration.xhtml)
content/browser/migration/migration.js (content/migration.js)
content/browser/aboutWelcomeBack.xhtml (content/aboutWelcomeBack.xhtml)
content/browser/migration/migration-dialog-window.html (content/migration-dialog-window.html)
content/browser/migration/migration-dialog-window.js (content/migration-dialog-window.js)

View file

@ -41,11 +41,11 @@ add_task(async function test_entrypoints() {
await showThenCloseMigrationWizardViaEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.BOOKMARKS
);
let entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
let entrypointIndex = getEntrypointHistogramIndex(
MigrationUtils.MIGRATION_ENTRYPOINTS.BOOKMARKS
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
TelemetryTestUtils.assertHistogram(histogram, entrypointIndex, 1);
histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
@ -54,19 +54,19 @@ add_task(async function test_entrypoints() {
await showThenCloseMigrationWizardViaEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES
);
entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
entrypointIndex = getEntrypointHistogramIndex(
MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
TelemetryTestUtils.assertHistogram(histogram, entrypointIndex, 1);
histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
// Finally, check the fallback by passing in something invalid as an entrypoint.
await showThenCloseMigrationWizardViaEntrypoint(undefined);
entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
entrypointIndex = getEntrypointHistogramIndex(
MigrationUtils.MIGRATION_ENTRYPOINTS.UNKNOWN
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
TelemetryTestUtils.assertHistogram(histogram, entrypointIndex, 1);
});

View file

@ -485,3 +485,50 @@ function assertQuantitiesShown(
}
}
}
/**
* Translates an entrypoint string into the proper numeric value for the
* FX_MIGRATION_ENTRY_POINT_CATEGORICAL histogram.
*
* @param {string} entrypoint
* The entrypoint to translate from MIGRATION_ENTRYPOINTS.
* @returns {number}
* The numeric index value for the FX_MIGRATION_ENTRY_POINT_CATEGORICAL
* histogram.
*/
function getEntrypointHistogramIndex(entrypoint) {
switch (entrypoint) {
case MigrationUtils.MIGRATION_ENTRYPOINTS.FIRSTRUN: {
return 1;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.FXREFRESH: {
return 2;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.PLACES: {
return 3;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.PASSWORDS: {
return 4;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.NEWTAB: {
return 5;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.FILE_MENU: {
return 6;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.HELP_MENU: {
return 7;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.BOOKMARKS_TOOLBAR: {
return 8;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES: {
return 9;
}
case MigrationUtils.MIGRATION_ENTRYPOINTS.UNKNOWN:
// Intentional fall-through
default: {
return 0; // Unknown
}
}
}

View file

@ -370,7 +370,7 @@ const OPTIN_DEFAULT = {
type: "text",
text: {
string_id:
"shopping-onboarding-opt-in-privacy-policy-and-terms-of-use",
"shopping-onboarding-opt-in-privacy-policy-and-terms-of-use2",
},
link_keys: ["privacy_policy", "terms_of_use"],
font_styles: "legal",

View file

@ -16,7 +16,7 @@ describe("LinkParagraph component", () => {
text_content={{
text: {
string_id:
"shopping-onboarding-opt-in-privacy-policy-and-terms-of-use",
"shopping-onboarding-opt-in-privacy-policy-and-terms-of-use2",
},
link_keys: ["privacy_policy"],
font_styles: "legal",
@ -53,7 +53,7 @@ describe("LinkParagraph component", () => {
text_content: {
text: {
string_id:
"shopping-onboarding-opt-in-privacy-policy-and-terms-of-use",
"shopping-onboarding-opt-in-privacy-policy-and-terms-of-use2",
},
link_keys: ["privacy_policy", "terms_of_use"],
font_styles: "legal",

View file

@ -44,7 +44,7 @@ skip-if = ["os != 'mac'"] # Mac-only functionality
["browser_bookmarkProperties_speculativeConnection.js"]
["browser_bookmarkProperties_xulStore.js"]
fail-if = ["a11y_checks"] # Bug 1854443 tree#bookmarks-view may not be labeled
skip-if = ["a11y_checks"] # Bugs 1854443 and 1858041 to investigate intermittent a11y_checks results
["browser_bookmark_add_tags.js"]
https_first_disabled = true

View file

@ -347,6 +347,11 @@ export class ShoppingSidebarChild extends RemotePageChild {
return;
}
if (!recommendationData.length) {
// We tried to fetch an ad, but didn't get one.
Glean.shopping.surfaceNoAdsAvailable.record();
}
this.sendToContent("UpdateRecommendations", {
recommendationData,
});

View file

@ -130,7 +130,7 @@ class AnalysisExplainer extends MozLitElement {
})}"
></p>
<p
data-l10n-id="shopping-analysis-explainer-learn-more"
data-l10n-id="shopping-analysis-explainer-learn-more2"
@click=${this.handleReviewQualityUrlClicked}
>
<a

View file

@ -539,3 +539,22 @@ shopping:
- fx-desktop-shopping-eng@mozilla.com
send_in_pings:
- events
surface_no_ads_available:
type: event
description: |
On a supported product page, the review checker showed analysis, and
review checker ads were enabled, but when we tried to fetch an ad from
the ad server, no ad was available.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1855811
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1855811
data_sensitivity:
- interaction
expires: 122
notification_emails:
- betling@mozilla.com
- fx-desktop-shopping-eng@mozilla.com
send_in_pings:
- events

View file

@ -85,17 +85,17 @@
<html:moz-button-group class="panel-footer translations-panel-footer">
<button id="translations-panel-restore-button"
class="panel-footer-button"
class="footer-button"
oncommand="TranslationsPanel.onRestore(event);"
data-l10n-id="translations-panel-restore-button">
</button>
<button id="translations-panel-cancel"
class="panel-footer-button"
class="footer-button"
oncommand="TranslationsPanel.onCancel(event);"
data-l10n-id="translations-panel-translate-cancel">
</button>
<button id="translations-panel-translate"
class="panel-footer-button"
class="footer-button"
oncommand="TranslationsPanel.onTranslate(event);"
data-l10n-id="translations-panel-translate-button"
default="true">
@ -127,12 +127,12 @@
<html:moz-button-group class="panel-footer translations-panel-footer">
<button id="translations-panel-change-source-language"
class="panel-footer-button"
class="footer-button"
oncommand="TranslationsPanel.onChangeSourceLanguage(event);"
data-l10n-id="translations-panel-error-change-button">
</button>
<button id="translations-panel-dismiss-error"
class="panel-footer-button"
class="footer-button"
oncommand="TranslationsPanel.onCancel(event);"
data-l10n-id="translations-panel-error-dismiss-button"
default="true">

View file

@ -95,6 +95,9 @@ async function testVal(aExpected, overflowSide = "") {
}
add_task(async function () {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.trimHttps", false]],
});
// We use a new tab for the test to be sure all the tab switching and loading
// is complete before starting, otherwise onLocationChange for this tab could
// override the value we set with an empty value.

View file

@ -89,7 +89,11 @@ add_task(async function url_with_additional_query_params() {
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, expectedSearchUrl);
await browserLoadedPromise;
Assert.equal(gURLBar.value, expectedSearchUrl, `URL should be in URL bar`);
Assert.equal(
gURLBar.value,
UrlbarTestUtils.trimURL(expectedSearchUrl),
`URL should be in URL bar`
);
Assert.equal(
gURLBar.getAttribute("pageproxystate"),
"valid",

View file

@ -92,7 +92,7 @@ add_task(async function generic_popup_when_persist_is_enabled() {
Assert.equal(
gURLBar.value,
expectedSearchUrl,
UrlbarTestUtils.trimURL(expectedSearchUrl),
"Search url should be in the urlbar."
);

View file

@ -73,7 +73,7 @@ add_task(async function revert() {
Assert.equal(
gURLBar.value,
expectedSearchUrl,
UrlbarTestUtils.trimURL(expectedSearchUrl),
`Urlbar should have the reverted URI ${expectedSearchUrl} as its value.`
);
@ -112,7 +112,7 @@ add_task(async function revert_and_change_tab() {
);
Assert.equal(
gURLBar.value,
expectedSearchUrl,
UrlbarTestUtils.trimURL(expectedSearchUrl),
`Urlbar should have ${expectedSearchUrl} as value.`
);

View file

@ -61,7 +61,11 @@ add_task(async function non_default_search() {
EventUtils.synthesizeKey("KEY_Enter");
await browserLoadedPromise;
Assert.equal(gURLBar.value, expectedSearchUrl, `URL should be in URL bar`);
Assert.equal(
gURLBar.value,
UrlbarTestUtils.trimURL(expectedSearchUrl),
`URL should be in URL bar`
);
Assert.equal(
gURLBar.getAttribute("pageproxystate"),
"valid",

View file

@ -33,7 +33,7 @@ var tests = [
gURLBar.handleRevert();
is(
gURLBar.value,
"example.com",
UrlbarTestUtils.trimURL("http://example.com"),
"URL bar had user/pass stripped after reverting"
);
gBrowser.removeTab(tab);
@ -49,7 +49,7 @@ var tests = [
closeToolbarCustomizationUI(function () {
is(
win.gURLBar.value,
"example.com",
UrlbarTestUtils.trimURL("http://example.com"),
"URL bar had user/pass stripped after customize"
);
win.close();
@ -70,7 +70,7 @@ var tests = [
tab.linkedBrowser.stop();
is(
gURLBar.value,
"example.com",
UrlbarTestUtils.trimURL("http://example.com"),
"URL bar had user/pass stripped after load error"
);
gBrowser.removeTab(tab);
@ -90,7 +90,7 @@ function loadTabInWindow(win, callback) {
info("Tab loaded");
is(
win.gURLBar.value,
"example.com",
UrlbarTestUtils.trimURL("http://example.com"),
"URL bar had user/pass stripped initially"
);
callback(tab);

View file

@ -76,7 +76,7 @@ add_task(async function () {
gURLBar.value = longURL;
is(
gURLBar.value,
longURL.replace(/^http:\/\//, ""),
UrlbarTestUtils.trimURL(longURL),
"Urlbar value has http:// stripped"
);
await expectTooltip(longURL);

View file

@ -12,7 +12,11 @@ function test() {
);
is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI");
is(gURLBar.value, URI, "location bar value matches test URI");
is(
gURLBar.value,
UrlbarTestUtils.trimURL(URI),
"location bar value matches test URI"
);
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
gBrowser.removeCurrentTab({ skipPermitUnload: true });
@ -23,7 +27,7 @@ function test() {
);
is(
gURLBar.value,
URI,
UrlbarTestUtils.trimURL(URI),
"location bar value matches test URI after switching tabs"
);
@ -36,7 +40,7 @@ function test() {
);
is(
gURLBar.value,
URI,
UrlbarTestUtils.trimURL(URI),
"location bar value matches test URI as the page has loaded"
);

View file

@ -442,7 +442,7 @@ add_task(async function buttons() {
);
Assert.equal(
gURLBar.value,
initialTabUrl,
UrlbarTestUtils.trimURL(initialTabUrl),
"Sanity check: input.value should be the initial URL initially"
);
@ -527,7 +527,7 @@ add_task(async function buttons() {
);
Assert.equal(
gURLBar.value,
state.value,
UrlbarTestUtils.trimURL(state.value),
"input.value should be as expected"
);
});

View file

@ -31,14 +31,14 @@ protections-panel-etp-off-header = Enhanced Tracking Protection is OFF for this
## custom element code.
## $host (String): the hostname of the site that is being displayed.
protections-panel-etp-on-toggle =
protections-panel-etp-toggle-on =
.label = Enhanced Tracking Protection
.description = On for this site
.aria-label = Disable protections for { $host }
protections-panel-etp-off-toggle =
.aria-label = Enhanced Tracking Protection: On for { $host }
protections-panel-etp-toggle-off =
.label = Enhanced Tracking Protection
.description = Off for this site
.aria-label = Enable protections for { $host }
.aria-label = Enhanced Tracking Protection: Off for { $host }
# The link to be clicked to open the sub-panel view
protections-panel-site-not-working = Site not working?

View file

@ -13,7 +13,7 @@ shopping-beta-marker = Beta
# Any changes to shopping-main-container-title and
# shopping-beta-marker should also be reflected here.
shopping-a11y-header =
.aria-label = Review checker - beta
.aria-label = Review Checker - beta
shopping-close-button =
.title = Close
# This string is for notifying screen reader users that the
@ -92,11 +92,11 @@ shopping-show-less-button = Show less
shopping-settings-label =
.label = Settings
shopping-settings-recommendations-toggle =
.label = Show ads in review checker
.label = Show ads in Review Checker
shopping-settings-recommendations-learn-more2 =
Youll see occasional ads for relevant products. We only advertise products with reliable reviews. <a data-l10n-name="review-quality-url">Learn more</a>
shopping-settings-opt-out-button = Turn off review checker
powered-by-fakespot = Review checker is powered by <a data-l10n-name="fakespot-link">{ -fakespot-brand-full-name }</a>.
shopping-settings-opt-out-button = Turn off Review Checker
powered-by-fakespot = Review Checker is powered by <a data-l10n-name="fakespot-link">{ -fakespot-brand-full-name }</a>.
## Strings for the adjusted rating component
@ -121,8 +121,8 @@ shopping-analysis-explainer-grades-intro =
We assign each products reviews a <strong>letter grade</strong> from A to F.
shopping-analysis-explainer-adjusted-rating-description =
The <strong>adjusted rating</strong> is based only on reviews we believe to be reliable.
shopping-analysis-explainer-learn-more =
Learn more about <a data-l10n-name="review-quality-url">how { -fakespot-brand-full-name } determines review quality</a>.
shopping-analysis-explainer-learn-more2 =
Learn more about <a data-l10n-name="review-quality-url">how { -fakespot-brand-name } determines review quality</a>.
# This string includes the short brand name of one of the three supported
# websites, which will be inserted without being translated.
@ -137,15 +137,15 @@ shopping-analysis-explainer-review-grading-scale-unreliable = Unreliable reviews
## Strings for UrlBar button
shopping-sidebar-open-button2 =
.tooltiptext = Open review checker
.tooltiptext = Open Review Checker
shopping-sidebar-close-button2 =
.tooltiptext = Close review checker
.tooltiptext = Close Review Checker
## Strings for the unanalyzed product card.
## The word 'analyzer' when used here reflects what this tool is called on
## fakespot.com. If possible, a different word should be used for the Fakespot
## tool (the Fakespot by Mozilla 'analyzer') other than 'checker', which is
## used in the name of the Firefox feature ('Review checker'). If that is not
## used in the name of the Firefox feature ('Review Checker'). If that is not
## possible - if these terms are not meaningfully different - that is OK.
shopping-unanalyzed-product-header-2 = No info about these reviews yet
@ -161,7 +161,7 @@ ad-by-fakespot = Ad by { -fakespot-brand-name }
## Shopping survey strings.
shopping-survey-headline = Help improve { -brand-product-name }
shopping-survey-question-one = How satisfied are you with the review checker experience in { -brand-product-name }?
shopping-survey-question-one = How satisfied are you with the Review Checker experience in { -brand-product-name }?
shopping-survey-q1-radio-1-label = Very satisfied
shopping-survey-q1-radio-2-label = Satisfied
@ -169,7 +169,7 @@ shopping-survey-q1-radio-3-label = Neutral
shopping-survey-q1-radio-4-label = Dissatisfied
shopping-survey-q1-radio-5-label = Very dissatisfied
shopping-survey-question-two = Does the review checker make it easier for you to make purchase decisions?
shopping-survey-question-two = Does the Review Checker make it easier for you to make purchase decisions?
shopping-survey-q2-radio-1-label = Yes
shopping-survey-q2-radio-2-label = No
@ -185,13 +185,13 @@ shopping-survey-thanks =
## "price tag" refers to the price tag icon displayed in the address bar to
## access the feature.
shopping-callout-closed-opted-in-subtitle = Get back to <strong>review checker</strong> whenever you see the price tag.
shopping-callout-closed-opted-in-subtitle = Get back to <strong>Review Checker</strong> whenever you see the price tag.
shopping-callout-pdp-opted-in-title = Are these reviews reliable? Find out fast.
shopping-callout-pdp-opted-in-subtitle = Open review checker to see an adjusted rating with unreliable reviews removed. Plus, see highlights from recent authentic reviews.
shopping-callout-pdp-opted-in-subtitle = Open Review Checker to see an adjusted rating with unreliable reviews removed. Plus, see highlights from recent authentic reviews.
shopping-callout-closed-not-opted-in-title = One click to reliable reviews
shopping-callout-closed-not-opted-in-subtitle = Give review checker a try whenever you see the price tag. Get insights from real shoppers quickly — before you buy.
shopping-callout-closed-not-opted-in-subtitle = Give Review Checker a try whenever you see the price tag. Get insights from real shoppers quickly — before you buy.
## Onboarding message strings.
@ -202,10 +202,10 @@ shopping-onboarding-headline = Try our trusted guide to product reviews
# $currentSite (str) - The current shopping page name
# $secondSite (str) - A second shopping page name
# $thirdSite (str) - A third shopping page name
shopping-onboarding-dynamic-subtitle-1 = See how reliable product reviews are on <b>{ $currentSite }</b> before you buy. Review checker, an experimental feature from { -brand-product-name }, is built right into the browser. It works on <b>{ $secondSite }</b> and <b>{ $thirdSite }</b>, too.
shopping-onboarding-dynamic-subtitle-1 = See how reliable product reviews are on <b>{ $currentSite }</b> before you buy. Review Checker, an experimental feature from { -brand-product-name }, is built right into the browser. It works on <b>{ $secondSite }</b> and <b>{ $thirdSite }</b>, too.
shopping-onboarding-body = Using the power of { -fakespot-brand-full-name }, we help you avoid biased and inauthentic reviews. Our AI model is always improving to protect you as you shop. <a data-l10n-name="learn_more">Learn more</a>
shopping-onboarding-opt-in-privacy-policy-and-terms-of-use = By selecting “{ shopping-onboarding-opt-in-button }“ you agree to { -fakespot-brand-full-name }s <a data-l10n-name="privacy_policy">privacy policy</a> and <a data-l10n-name="terms_of_use">terms of use.</a>
shopping-onboarding-opt-in-privacy-policy-and-terms-of-use2 = By selecting “{ shopping-onboarding-opt-in-button }“ you agree to { -fakespot-brand-name }s <a data-l10n-name="privacy_policy">privacy policy</a> and <a data-l10n-name="terms_of_use">terms of use.</a>
shopping-onboarding-opt-in-button = Yes, try it
shopping-onboarding-not-now-button = Not now
shopping-onboarding-dialog-close-button =

View file

@ -519,7 +519,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "0c61d58a873eb41ad722d87dcdbe9c2ab60f3da0"
"revision": "9aa18cd3d907df422ac066deb7b6c2cdc2bc19e8"
},
"es-MX": {
"pin": false,

View file

@ -99,13 +99,13 @@ export const ThemeVariableMap = [
},
],
[
"--autocomplete-popup-highlight-background",
"--urlbarView-highlight-background",
{
lwtProperty: "popup_highlight",
},
],
[
"--autocomplete-popup-highlight-color",
"--urlbarView-highlight-color",
{
lwtProperty: "popup_highlight_text",
},
@ -128,14 +128,12 @@ export const ThemeVariableMap = [
"--sidebar-text-color",
{
lwtProperty: "sidebar_text",
optionalElementID: "sidebar-box",
},
],
[
"--sidebar-border-color",
{
lwtProperty: "sidebar_border",
optionalElementID: "browser",
},
],
[

View file

@ -50,7 +50,7 @@
"input_background": "#42414D",
"input_color": "rgb(251,251,254)",
"input_border": "#8f8f9d",
"autocomplete_popup_separator": "rgb(82,82,94)",
"urlbar_popup_separator": "rgb(82,82,94)",
"appmenu_update_icon_color": "#54FFBD",
"appmenu_info_icon_color": "#80EBFF",
"tab_icon_overlay_stroke": "rgb(66,65,77)",
@ -77,7 +77,7 @@
"input_background": "--input-bgcolor",
"input_color": "--input-color",
"input_border": "--input-border-color",
"autocomplete_popup_separator": "--autocomplete-popup-separator-color",
"urlbar_popup_separator": "--urlbarView-separator-color",
"zoom_controls": "--zoom-controls-bgcolor",
"appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
"appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",

View file

@ -47,8 +47,8 @@
"button_primary_color": "rgb(251, 251, 254)",
"input_color": "rgb(21,20,26)",
"input_background": "rgb(255,255,255)",
"autocomplete_popup_hover": "rgb(240,240,244)",
"autocomplete_popup_separator": "rgb(240,240,244)",
"urlbar_popup_hover": "rgb(240,240,244)",
"urlbar_popup_separator": "rgb(240,240,244)",
"appmenu_update_icon_color": "#2AC3A2",
"appmenu_info_icon_color": "#0090ED",
"tab_icon_overlay_stroke": "rgb(255,255,255)",
@ -79,8 +79,8 @@
"input_background": "--input-bgcolor",
"input_color": "--input-color",
"input_border": "--input-border-color",
"autocomplete_popup_hover": "--autocomplete-popup-hover-background",
"autocomplete_popup_separator": "--autocomplete-popup-separator-color",
"urlbar_popup_hover": "--urlbarView-hover-background",
"urlbar_popup_separator": "--urlbarView-separator-color",
"appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
"appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",
"tab_icon_overlay_stroke": "--tab-icon-overlay-stroke",

View file

@ -74,27 +74,26 @@
--toolbarbutton-icon-fill: light-dark(rgb(91, 91, 102), rgb(251, 251, 254));
--toolbarbutton-icon-fill-attention: light-dark(rgb(0, 97, 224), rgb(0, 221, 255));
--toolbar-field-border-color: transparent;
--toolbar-field-background-color: light-dark(rgb(240, 240, 244), rgb(28, 27, 34));
--toolbar-field-color: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
--toolbar-field-focus-background-color: light-dark(white, rgb(66, 65, 77));
--toolbar-field-focus-color: light-dark(black, rgb(251, 251, 254));
--urlbarView-action-color: light-dark(rgb(91, 91, 102), rgb(191, 191, 201));
--urlbarView-separator-color: light-dark(rgb(240, 240, 244), rgb(82, 82, 94));
--urlbarView-highlight-background: light-dark(#e0e0e6, rgb(43,42,51));
--urlbarView-highlight-color: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
--urlbarView-hover-background: light-dark(rgb(240, 240, 244), var(--arrowpanel-dimmed));
--urlbar-box-bgcolor: light-dark(white, rgb(66, 65, 77));
--urlbar-icon-fill-opacity: 0.72;
@media (prefers-color-scheme: dark) {
--urlbar-icon-fill-opacity: 1;
}
--autocomplete-popup-separator-color: light-dark(rgb(240, 240, 244), rgb(82, 82, 94));
--toolbar-field-border-color: transparent;
--chrome-content-separator-color: light-dark(rgb(204, 204, 204), hsl(240, 5%, 5%));
--toolbar-field-background-color: light-dark(rgb(240, 240, 244), rgb(28, 27, 34));
--toolbar-field-color: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
--toolbar-field-focus-background-color: light-dark(white, rgb(66, 65, 77));
--toolbar-field-focus-color: light-dark(black, rgb(251, 251, 254));
--autocomplete-popup-highlight-background: light-dark(#e0e0e6, rgb(43,42,51));
--autocomplete-popup-highlight-color: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
--autocomplete-popup-hover-background: light-dark(rgb(240, 240, 244), var(--arrowpanel-dimmed));
--link-color: light-dark(rgb(0, 97, 224), rgb(0, 221, 255));
}
}

View file

@ -66,9 +66,6 @@
--checkbox-checked-hover-bgcolor: var(--button-primary-hover-bgcolor);
--checkbox-checked-active-bgcolor: var(--button-primary-active-bgcolor);
--autocomplete-popup-hover-background: var(--arrowpanel-dimmed);
--autocomplete-popup-separator-color: color-mix(in srgb, currentColor 14%, transparent);
--identity-box-margin-inline: 4px;
--urlbar-box-bgcolor: var(--button-bgcolor);
--urlbar-box-focus-bgcolor: var(--button-bgcolor);
@ -111,7 +108,6 @@
@media (prefers-contrast) {
:root {
--autocomplete-popup-separator-color: color-mix(in srgb, currentColor 86%, transparent);
--urlbar-icon-fill-opacity: 1;
--checkbox-checked-border-color: var(--checkbox-checked-bgcolor);
}

View file

@ -87,7 +87,7 @@
}
#viewButton > .button-box > .button-menu-dropmarker {
appearance: none !important;
appearance: none;
display: flex;
list-style-image: url("chrome://global/skin/icons/arrow-down-12.svg");
width: 12px;

View file

@ -91,13 +91,13 @@
/* We don't handle `:active` because it doesn't work on the search or settings
buttons due to event.preventDefault() in SearchOneOffs._on_mousedown(). */
.searchbar-engine-one-off-item:not([selected]):hover {
background-color: var(--autocomplete-popup-hover-background);
background-color: var(--urlbarView-hover-background);
color: inherit;
}
.searchbar-engine-one-off-item[selected] {
background-color: var(--autocomplete-popup-highlight-background);
color: var(--autocomplete-popup-highlight-color);
background-color: var(--urlbarView-highlight-background);
color: var(--urlbarView-highlight-color);
}
.searchbar-engine-one-off-item > .button-box > .button-text {
@ -120,7 +120,7 @@
}
.search-panel-tree > .autocomplete-richlistitem:hover {
background-color: var(--autocomplete-popup-hover-background);
background-color: var(--urlbarView-hover-background);
}
.search-panel-tree > .autocomplete-richlistitem > .ac-type-icon {
@ -146,7 +146,7 @@
margin: var(--panel-separator-margin);
padding: 0;
border: 0;
border-top: 1px solid var(--autocomplete-popup-separator-color);
border-top: 1px solid var(--urlbarView-separator-color);
color: inherit;
}

View file

@ -126,12 +126,12 @@
}
&:hover:not([disabled]):not([selected]) {
background-color: var(--autocomplete-popup-hover-background);
background-color: var(--urlbarView-hover-background);
}
&[selected] {
background-color: var(--autocomplete-popup-highlight-background);
color: var(--autocomplete-popup-highlight-color);
background-color: var(--urlbarView-highlight-background);
color: var(--urlbarView-highlight-color);
}
> .urlbarView-label {

View file

@ -7,8 +7,11 @@
@namespace html url("http://www.w3.org/1999/xhtml");
:root {
--autocomplete-popup-highlight-background: SelectedItem;
--autocomplete-popup-highlight-color: SelectedItemText;
--urlbarView-hover-background: var(--arrowpanel-dimmed);
--urlbarView-separator-color: color-mix(in srgb, currentColor 14%, transparent);
--urlbarView-highlight-background: SelectedItem;
--urlbarView-highlight-color: SelectedItemText;
--urlbarView-secondary-text-color: color-mix(in srgb, currentColor 73%, transparent);
@ -39,12 +42,12 @@
&:-moz-lwtheme {
--urlbarView-action-color: light-dark(rgb(91, 91, 102), rgb(191, 191, 201));
/* Fallback colors for when themes don't specify popup_highlight / popup_highlight_text. */
--autocomplete-popup-highlight-background: light-dark(rgb(0, 99, 255), rgb(0, 99, 225));
--autocomplete-popup-highlight-color: white;
--urlbarView-highlight-background: light-dark(rgb(0, 99, 255), rgb(0, 99, 225));
--urlbarView-highlight-color: white;
}
@media (prefers-contrast) {
--urlbarView-separator-color: color-mix(in srgb, currentColor 86%, transparent);
--urlbarView-result-button-background-opacity: 100%;
--urlbarView-secondary-text-color: currentColor;
}
@ -76,7 +79,7 @@
margin-inline: calc(-1 * var(--urlbarView-row-gutter));
#urlbar[open] > .urlbarView > .urlbarView-body-outer > & {
border-top: 1px solid var(--autocomplete-popup-separator-color);
border-top: 1px solid var(--urlbarView-separator-color);
}
}
@ -106,12 +109,12 @@
background-clip: padding-box;
&[row-selectable]:not([selected]):hover {
background-color: var(--autocomplete-popup-hover-background);
background-color: var(--urlbarView-hover-background);
}
&[selected] {
background-color: var(--autocomplete-popup-highlight-background);
color: var(--autocomplete-popup-highlight-color);
background-color: var(--urlbarView-highlight-background);
color: var(--urlbarView-highlight-color);
}
:root:not([uidensity=compact]) &:not([type=tip], [type=dynamic]) {
@ -320,7 +323,7 @@
&[selected] {
outline: var(--focus-outline);
outline-color: var(--autocomplete-popup-highlight-color);
outline-color: var(--urlbarView-highlight-color);
outline-offset: 1px;
}
@ -424,12 +427,12 @@
border-radius: 0;
&:not(:last-child) {
border-bottom: 1px solid var(--autocomplete-popup-separator-color);
border-bottom: 1px solid var(--urlbarView-separator-color);
margin-bottom: 4px;
}
&:not(:first-child) {
border-top: 1px solid var(--autocomplete-popup-separator-color);
border-top: 1px solid var(--urlbarView-separator-color);
margin-top: 4px;
}
@ -832,7 +835,7 @@
}
.urlbarView:not([noresults]) > .search-one-offs:not([hidden]) {
border-top: 1px solid var(--autocomplete-popup-separator-color);
border-top: 1px solid var(--urlbarView-separator-color);
}
:root[uidensity=touch] #urlbar .search-one-offs:not([hidden]) {
@ -942,6 +945,6 @@
}
#PopupSearchAutoComplete .autocomplete-richlistitem[selected] {
background: var(--autocomplete-popup-highlight-background);
color: var(--autocomplete-popup-highlight-color);
background: var(--urlbarView-highlight-background);
color: var(--urlbarView-highlight-color);
}

View file

@ -148,6 +148,7 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
skip-if = ["a11y_checks"] # Bug 1849028 and 1849179 for causing crashes
["browser_aboutdebugging_fenix_runtime_node_picker.js"]
skip-if = ["a11y_checks"] # Bug 1849028, 1858041, and 1849179 for causing crashes
["browser_aboutdebugging_hidden_addons.js"]
@ -248,6 +249,7 @@ skip-if = ["a11y_checks"] # Bug 1849028 and 1849179 for causing crashes
skip-if = ["a11y_checks"] # Bug 1849028 and 1849179 for causing crashes
["browser_aboutdebugging_telemetry_inspect.js"]
skip-if = ["a11y_checks"] # Bug 1849028 and 1849179 for causing crashes
["browser_aboutdebugging_telemetry_navigate.js"]

View file

@ -49,7 +49,7 @@
</head>
<body>
<main id="mount" class="theme-body"></main>
<div id="mount" class="theme-body"></div>
<script
src="chrome://devtools/content/shared/theme-switching.js"
></script>

View file

@ -3,7 +3,7 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
import React, { Component } from "react";
import { div } from "react-dom-factories";
import { div, main } from "react-dom-factories";
import PropTypes from "prop-types";
import { connect } from "../utils/connect";
import { prefs } from "../utils/prefs";
@ -208,7 +208,7 @@ class App extends Component {
const { startPanelCollapsed, endPanelCollapsed } = this.props;
const { endPanelSize, startPanelSize } = this.state;
const horizontal = this.isHorizontal();
return div(
return main(
{
className: "editor-pane",
},
@ -279,6 +279,7 @@ class App extends Component {
splitterSize: 1,
onResizeEnd: num => {
prefs.startPanelSize = num;
this.triggerEditorPaneResize();
},
startPanelCollapsed: startPanelCollapsed,
startPanel: React.createElement(PrimaryPanes, {

View file

@ -79,9 +79,9 @@ class InlinePreviewRow extends PureComponent {
})
)
),
this.widgetNode
);
this.widgetNode,
() => {
// Only set the codeMirror bookmark once React rendered the element into this.widgetNode
this.bookmark = editor.codeMirror.setBookmark(
{
line,
@ -90,6 +90,8 @@ class InlinePreviewRow extends PureComponent {
this.widgetNode
);
}
);
}
render() {
return null;

View file

@ -186,10 +186,23 @@ class Editor extends PureComponent {
}
const { codeMirror } = editor;
const codeMirrorWrapper = codeMirror.getWrapperElement();
this.abortController = new window.AbortController();
// CodeMirror refreshes its internal state on window resize, but we need to also
// refresh it when the side panels are resized.
// We could have a ResizeObserver instead, but we wouldn't be able to differentiate
// between window resize and side panel resize and as a result, might refresh
// codeMirror twice, which is wasteful.
window.document
.querySelector(".editor-pane")
.addEventListener("resizeend", () => codeMirror.refresh(), {
signal: this.abortController.signal,
});
codeMirror.on("gutterClick", this.onGutterClick);
const codeMirrorWrapper = codeMirror.getWrapperElement();
// Set code editor wrapper to be focusable
codeMirrorWrapper.tabIndex = 0;
codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e));
@ -260,6 +273,11 @@ class Editor extends PureComponent {
shortcuts.off(L10N.getStr("toggleBreakpoint.key"));
shortcuts.off(L10N.getStr("toggleCondPanel.breakpoint.key"));
shortcuts.off(L10N.getStr("toggleCondPanel.logPoint.key"));
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
}
getCurrentLine() {

View file

@ -98,6 +98,7 @@ export default class FrameComponent extends Component {
frame: PropTypes.object.isRequired,
getFrameTitle: PropTypes.func,
hideLocation: PropTypes.bool.isRequired,
isInGroup: PropTypes.bool,
panel: PropTypes.oneOf(["debugger", "webconsole"]).isRequired,
selectFrame: PropTypes.func.isRequired,
selectedFrame: PropTypes.object,
@ -149,6 +150,7 @@ export default class FrameComponent extends Component {
getFrameTitle,
disableContextMenu,
shouldDisplayOriginalLocation,
isInGroup,
} = this.props;
const { l10n } = this.context;
@ -193,7 +195,10 @@ export default class FrameComponent extends Component {
className: "clipboard-only",
})
),
this.isSelectable && React.createElement(FrameIndent, null),
this.isSelectable &&
React.createElement(FrameIndent, {
indentLevel: isInGroup ? 2 : 1,
}),
React.createElement(FrameTitle, {
frame,
options: {

View file

@ -4,9 +4,10 @@
import React from "react";
export default function FrameIndent() {
export default function FrameIndent({ indentLevel = 1 } = {}) {
// \xA0 represents the non breakable space &nbsp;
const nonBreakableSpaces = "\xA0\xA0\xA0\xA0";
const indentWidth = 4 * indentLevel;
const nonBreakableSpaces = "\xA0".repeat(indentWidth);
return React.createElement(
"span",
{

View file

@ -111,15 +111,7 @@ export default class Group extends Component {
{
className: "frames-list",
},
group.reduce((acc, frame, i) => {
if (this.isSelectable) {
acc.push(
React.createElement(FrameIndent, {
key: `frame-indent-${i}`,
})
);
}
return acc.concat(
group.map(frame =>
React.createElement(FrameComponent, {
frame: frame,
showFrameContextMenu: showFrameContextMenu,
@ -133,9 +125,9 @@ export default class Group extends Component {
getFrameTitle: getFrameTitle,
disableContextMenu: disableContextMenu,
panel: panel,
isInGroup: true,
})
);
}, [])
)
);
}

View file

@ -11,7 +11,9 @@ exports[`Frame getFrameTitle 1`] = `
tabIndex={0}
title="Jump to https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com/assets/src/js/foo-view.js:10"
>
<FrameIndent />
<FrameIndent
indentLevel={1}
/>
<FrameTitle
frame={
Object {
@ -301,7 +303,9 @@ exports[`Frame library frame 1`] = `
role="listitem"
tabIndex={0}
>
<FrameIndent />
<FrameIndent
indentLevel={1}
/>
<FrameTitle
frame={
Object {
@ -593,7 +597,9 @@ exports[`Frame user frame (not selected) 1`] = `
role="listitem"
tabIndex={0}
>
<FrameIndent />
<FrameIndent
indentLevel={1}
/>
<FrameTitle
frame={
Object {
@ -883,7 +889,9 @@ exports[`Frame user frame 1`] = `
role="listitem"
tabIndex={0}
>
<FrameIndent />
<FrameIndent
indentLevel={1}
/>
<FrameTitle
frame={
Object {

View file

@ -324,9 +324,6 @@ exports[`Group passes the getFrameTitle prop to the Frame components 1`] = `
<div
className="frames-list"
>
<FrameIndent
key="frame-indent-0"
/>
<Frame
disableContextMenu={false}
displayFullUrl={false}
@ -462,6 +459,7 @@ exports[`Group passes the getFrameTitle prop to the Frame components 1`] = `
}
getFrameTitle={[Function]}
hideLocation={true}
isInGroup={true}
key="1"
panel="webconsole"
selectFrame={[MockFunction]}
@ -598,9 +596,6 @@ exports[`Group passes the getFrameTitle prop to the Frame components 1`] = `
}
shouldMapDisplayName={false}
/>
<FrameIndent
key="frame-indent-1"
/>
<Frame
disableContextMenu={false}
displayFullUrl={false}
@ -736,6 +731,7 @@ exports[`Group passes the getFrameTitle prop to the Frame components 1`] = `
}
getFrameTitle={[Function]}
hideLocation={true}
isInGroup={true}
key="2"
panel="webconsole"
selectFrame={[MockFunction]}
@ -872,9 +868,6 @@ exports[`Group passes the getFrameTitle prop to the Frame components 1`] = `
}
shouldMapDisplayName={false}
/>
<FrameIndent
key="frame-indent-2"
/>
<Frame
disableContextMenu={false}
displayFullUrl={false}
@ -1010,6 +1003,7 @@ exports[`Group passes the getFrameTitle prop to the Frame components 1`] = `
}
getFrameTitle={[Function]}
hideLocation={true}
isInGroup={true}
key="3"
panel="webconsole"
selectFrame={[MockFunction]}
@ -1474,9 +1468,6 @@ exports[`Group renders group with anonymous functions 2`] = `
<div
className="frames-list"
>
<FrameIndent
key="frame-indent-0"
/>
<Frame
disableContextMenu={false}
displayFullUrl={false}
@ -1611,6 +1602,7 @@ exports[`Group renders group with anonymous functions 2`] = `
}
}
hideLocation={true}
isInGroup={true}
key="1"
panel="webconsole"
selectFrame={[MockFunction]}
@ -1747,9 +1739,6 @@ exports[`Group renders group with anonymous functions 2`] = `
}
shouldMapDisplayName={false}
/>
<FrameIndent
key="frame-indent-1"
/>
<Frame
disableContextMenu={false}
displayFullUrl={false}
@ -1884,6 +1873,7 @@ exports[`Group renders group with anonymous functions 2`] = `
}
}
hideLocation={true}
isInGroup={true}
key="2"
panel="webconsole"
selectFrame={[MockFunction]}
@ -2020,9 +2010,6 @@ exports[`Group renders group with anonymous functions 2`] = `
}
shouldMapDisplayName={false}
/>
<FrameIndent
key="frame-indent-2"
/>
<Frame
disableContextMenu={false}
displayFullUrl={false}
@ -2157,6 +2144,7 @@ exports[`Group renders group with anonymous functions 2`] = `
}
}
hideLocation={true}
isInGroup={true}
key="3"
panel="webconsole"
selectFrame={[MockFunction]}

View file

@ -269,6 +269,7 @@ class SecondaryPanes extends Component {
getWatchItem() {
return {
header: L10N.getStr("watchExpressions.header"),
id: "watch-expressions-pane",
className: "watch-expressions-pane",
buttons: this.watchExpressionHeaderButtons(),
component: React.createElement(Expressions, {
@ -287,6 +288,7 @@ class SecondaryPanes extends Component {
return {
header: L10N.getStr("xhrBreakpoints.header"),
id: "xhr-breakpoints-pane",
className: "xhr-breakpoints-pane",
buttons: this.xhrBreakpointsHeaderButtons(),
component: React.createElement(XHRBreakpoints, {
@ -303,6 +305,7 @@ class SecondaryPanes extends Component {
getCallStackItem() {
return {
header: L10N.getStr("callStack.header"),
id: "call-stack-pane",
className: "call-stack-pane",
component: React.createElement(Frames, {
panel: "debugger",
@ -317,6 +320,7 @@ class SecondaryPanes extends Component {
getThreadsItem() {
return {
header: L10N.getStr("threadsHeader"),
id: "threads-pane",
className: "threads-pane",
component: React.createElement(Threads, null),
opened: prefs.threadsVisible,
@ -338,6 +342,7 @@ class SecondaryPanes extends Component {
return {
header: L10N.getStr("breakpoints.header"),
id: "breakpoints-pane",
className: "breakpoints-pane",
buttons: this.breakpointsHeaderButtons(),
component: React.createElement(Breakpoints, {
@ -364,6 +369,7 @@ class SecondaryPanes extends Component {
return {
header: L10N.getStr("eventListenersHeader1"),
id: "event-listeners-pane",
className: "event-listeners-pane",
buttons: this.getEventButtons(),
component: React.createElement(EventListeners, null),
@ -379,6 +385,7 @@ class SecondaryPanes extends Component {
return {
header: L10N.getStr("domMutationHeader"),
id: "dom-mutations-pane",
className: "dom-mutations-pane",
buttons: [],
component: React.createElement(DOMMutationBreakpoints, null),

View file

@ -3,7 +3,7 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
import React, { cloneElement, Component } from "react";
import { li, h2, span, div, ul } from "react-dom-factories";
import { aside, h2, div, span } from "react-dom-factories";
import PropTypes from "prop-types";
import AccessibleImage from "./AccessibleImage";
@ -38,10 +38,12 @@ class Accordion extends Component {
renderContainer = (item, i) => {
const { opened } = item;
return li(
return aside(
{
className: item.className,
key: i,
key: item.id,
"aria-labelledby": item.id,
role: item.role,
},
h2(
{
@ -55,6 +57,7 @@ class Accordion extends Component {
}),
span(
{
id: item.id,
className: "header-label",
},
item.header
@ -79,7 +82,7 @@ class Accordion extends Component {
);
};
render() {
return ul(
return div(
{
className: "accordion",
},

View file

@ -11,6 +11,7 @@ describe("Accordion", () => {
const testItems = [
{
header: "Test Accordion Item 1",
id: "accordion-item-1",
className: "accordion-item-1",
component: React.createElement("div", null),
opened: false,
@ -18,6 +19,7 @@ describe("Accordion", () => {
},
{
header: "Test Accordion Item 2",
id: "accordion-item-2",
className: "accordion-item-2",
component: React.createElement("div", null),
buttons: React.createElement("button", null),
@ -26,6 +28,7 @@ describe("Accordion", () => {
},
{
header: "Test Accordion Item 3",
id: "accordion-item-3",
className: "accordion-item-3",
component: React.createElement("div", null),
opened: true,

View file

@ -1,12 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Accordion basic render 1`] = `
<ul
<div
className="accordion"
>
<li
<aside
aria-labelledby="accordion-item-1"
className="accordion-item-1"
key="0"
key="accordion-item-1"
>
<h2
className="_header"
@ -19,6 +20,7 @@ exports[`Accordion basic render 1`] = `
/>
<span
className="header-label"
id="accordion-item-1"
>
Test Accordion Item 1
</span>
@ -28,10 +30,11 @@ exports[`Accordion basic render 1`] = `
>
<div />
</div>
</li>
<li
</aside>
<aside
aria-labelledby="accordion-item-2"
className="accordion-item-2"
key="1"
key="accordion-item-2"
>
<h2
className="_header"
@ -44,6 +47,7 @@ exports[`Accordion basic render 1`] = `
/>
<span
className="header-label"
id="accordion-item-2"
>
Test Accordion Item 2
</span>
@ -54,10 +58,11 @@ exports[`Accordion basic render 1`] = `
<button />
</div>
</h2>
</li>
<li
</aside>
<aside
aria-labelledby="accordion-item-3"
className="accordion-item-3"
key="2"
key="accordion-item-3"
>
<h2
className="_header"
@ -70,6 +75,7 @@ exports[`Accordion basic render 1`] = `
/>
<span
className="header-label"
id="accordion-item-3"
>
Test Accordion Item 3
</span>
@ -79,6 +85,6 @@ exports[`Accordion basic render 1`] = `
>
<div />
</div>
</li>
</ul>
</aside>
</div>
`;

View file

@ -116,18 +116,32 @@ export function getTokenEnd(codeMirror, line, column) {
}
/**
* Given the dom element realted to the token, this gets its line and column.
* Given the dom element related to the token, this gets its line and column.
*
* @param {*} codeMirror
* @param {*} tokenEl
* @returns {Object} An object of the form { line, column }
*/
export function getTokenLocation(codeMirror, tokenEl) {
const { left, top, width, height } = tokenEl.getBoundingClientRect();
const { line, ch } = codeMirror.coordsChar({
left: left + width / 2,
top: top + height / 2,
});
// Get the quad (and not the bounding rect), as the span could wrap on multiple lines
// and the middle of the bounding rect may not be over the token:
// +───────────────────────+
// │ myLongVariableNa│
// │me + │
// +───────────────────────+
const { p1, p2, p3 } = tokenEl.getBoxQuads()[0];
const left = p1.x + (p2.x - p1.x) / 2;
const top = p1.y + (p3.y - p1.y) / 2;
const { line, ch } = codeMirror.coordsChar(
{
left,
top,
},
// Use the "window" context where the coordinates are relative to the top-left corner
// of the currently visible (scrolled) window.
// This enables codemirror also correctly handle wrappped lines in the editor.
"window"
);
return {
line: line + 1,

View file

@ -73,7 +73,7 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
["browser_dbg-breakpoints-in-evaled-sources.js"]
["browser_dbg-breakpoints-list.js"]
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
skip-if = ["a11y_checks"] # Bugs 1849028 and 1849230 for causing crashes
["browser_dbg-breakpoints-popup.js"]
skip-if = [
@ -392,6 +392,8 @@ skip-if = ["os == 'win'"]
["browser_dbg-preview-source-maps.js"]
skip-if = ["os == 'win'"]
["browser_dbg-preview-wrapped-lines.js"]
["browser_dbg-preview.js"]
skip-if = [
"os == 'win'",

View file

@ -0,0 +1,154 @@
/* 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/>. */
// Test that the tooltip previews are correct with wrapped editor lines.
"use strict";
const httpServer = createTestHTTPServer();
httpServer.registerContentType("html", "text/html");
httpServer.registerContentType("js", "application/javascript");
httpServer.registerPathHandler(
"/doc-wrapped-lines.html",
(request, response) => {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(`<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
const cs1 = getComputedStyle(document.documentElement);
const cs2 = getComputedStyle(document.documentElement);
// This line generates a very long inline-preview which is loaded a bit later after the
// initial positions for the page has been calculated
function add(a,b,k){var result=a+b;return k(result)}function sub(a,b,k){var result=a-b;return k(result)}function mul(a,b,k){var result=a*b;return k(result)}function div(a,b,k){var result=a/b;return k(result)}function arithmetic(){
add(4,4,function(a){
sub(a,2,function(b){mul(b,3,function(c){div(c,2,function(d){console.log("arithmetic",d)})})})})};
isNaN(cs1, cs2);
const foo = { prop: 0 };
const bar = Math.min(foo);
const myVeryLongVariableNameThatMayWrap = 42;
myVeryLongVariableNameThatMayWrap * 2;
debugger;
</script>
</head>
</html>
`);
}
);
const BASE_URL = `http://localhost:${httpServer.identity.primaryPort}/`;
add_task(async function () {
await pushPref("devtools.toolbox.footer.height", 500);
await pushPref("devtools.debugger.ui.editor-wrapping", true);
// Reset toolbox height and end panel size to avoid impacting other tests
registerCleanupFunction(() => {
Services.prefs.clearUserPref("debugger.end-panel-size");
});
const dbg = await initDebuggerWithAbsoluteURL(
`${BASE_URL}doc-wrapped-lines.html`
);
await waitForSources(dbg, "doc-wrapped-lines.html");
const onReloaded = reload(dbg);
await waitForPaused(dbg);
await assertPreviews(dbg, [
{
line: 13,
column: 18,
expression: "foo",
fields: [["prop", "0"]],
},
{
line: 14,
column: 18,
result: "NaN",
expression: "bar",
},
]);
info("Resize the editor until `myVeryLongVariableNameThatMayWrap` wraps");
// Use splitter to resize to make sure CodeMirror internal state is refreshed
// in such case (CodeMirror already handle window resize on its own).
const splitter = dbg.win.document.querySelectorAll(".splitter")[1];
const splitterOriginalX = splitter.getBoundingClientRect().left;
ok(splitter, "Got the splitter");
let longToken = getTokenElAtLine(
dbg,
"myVeryLongVariableNameThatMayWrap",
16
);
const longTokenBoundingClientRect = longToken.getBoundingClientRect();
await resizeSplitter(
dbg,
splitter,
longTokenBoundingClientRect.left + longTokenBoundingClientRect.width / 2
);
info("Wait until the token does wrap");
longToken = await waitFor(() => {
const token = getTokenElAtLine(
dbg,
"myVeryLongVariableNameThatMayWrap",
16
);
if (token.getBoxQuads().length === 1) {
return null;
}
return token;
});
longToken.scrollIntoView();
await assertPreviews(dbg, [
{
line: 16,
column: 13,
expression: "myVeryLongVariableNameThatMayWrap",
result: "42",
},
]);
// clearing the pref isn't enough to have consistent sizes between runs,
// so set it back to its original position
await resizeSplitter(dbg, splitter, splitterOriginalX);
await resume(dbg);
await onReloaded;
Services.prefs.clearUserPref("debugger.end-panel-size");
await wait(1000);
});
async function resizeSplitter(dbg, splitterEl, x) {
EventUtils.synthesizeMouse(splitterEl, 0, 0, { type: "mousedown" }, dbg.win);
// Resizing the editor should cause codeMirror to refresh
const cm = dbg.getCM();
const onEditorRefreshed = new Promise(resolve =>
cm.on("refresh", function onCmRefresh() {
cm.off("refresh", onCmRefresh);
resolve();
})
);
// Move the splitter of the secondary pane to the middle of the token,
// this should cause the token to wrap.
EventUtils.synthesizeMouseAtPoint(
x,
splitterEl.getBoundingClientRect().top + 10,
{ type: "mousemove" },
dbg.win
);
// Stop dragging
EventUtils.synthesizeMouseAtCenter(splitterEl, { type: "mouseup" }, dbg.win);
await onEditorRefreshed;
ok(true, "CodeMirror was refreshed when resizing the editor");
}

View file

@ -50,7 +50,7 @@ add_task(async function () {
{
line: 50,
column: 47,
expression: "Foo.#privateStatic",
expression: "#privateStatic",
fields: [
["first", `"a"`],
["second", `"b"`],

View file

@ -2172,11 +2172,25 @@ async function hoverAtPos(dbg, pos) {
}
function hoverToken(tokenEl) {
info(`Hovering on token "${tokenEl.innerText}"`);
info(`Hovering on token <${tokenEl.innerText}>`);
// We can't use synthesizeMouse(AtCenter) as it's using the element bounding client rect.
// But here, we might have a token that wraps on multiple line and the center of the
// bounding client rect won't actually hover the token.
// +───────────────────────+
// │ myLongVariableNa│
// │me + │
// +───────────────────────+
// Instead, we need to get the first quad.
const { p1, p2, p3 } = tokenEl.getBoxQuads()[0];
const x = p1.x + (p2.x - p1.x) / 2;
const y = p1.y + (p3.y - p1.y) / 2;
// This first event helps utils/editor/tokens.js to receive the right mouseover event
EventUtils.synthesizeMouseAtCenter(
tokenEl,
EventUtils.synthesizeMouseAtPoint(
x,
y,
{
type: "mouseover",
},
@ -2184,8 +2198,9 @@ function hoverToken(tokenEl) {
);
// This second event helps Popover to have :hover pseudoclass set on the token element
EventUtils.synthesizeMouseAtCenter(
tokenEl,
EventUtils.synthesizeMouseAtPoint(
x,
y,
{
type: "mousemove",
},
@ -2217,8 +2232,22 @@ async function closePreviewForToken(
//
// This helps utils/editor/tokens.js to receive the right mouseleave event.
// This is super important as it will then allow re-emitting a tokenenter event if you try to re-preview the same token!
EventUtils.synthesizeMouseAtCenter(
// We can't use synthesizeMouse(AtCenter) as it's using the element bounding client rect.
// But here, we might have a token that wraps on multiple line and the center of the
// bounding client rect won't actually hover the token.
// +───────────────────────+
// │ myLongVariableNa│
// │me + │
// +───────────────────────+
// Instead, we need to get the first quad.
const { p1, p2, p3 } = tokenEl.getBoxQuads()[0];
const x = p1.x + (p2.x - p1.x) / 2;
const y = p1.y + (p3.y - p1.y) / 2;
EventUtils.synthesizeMouseAtPoint(
tokenEl,
x,
y,
{
type: "mouseout",
},
@ -2246,9 +2275,20 @@ async function closePreviewForToken(
info("Preview closed");
}
// tryHovering will hover at a position every second until we
// see a preview element (popup, tooltip) appear. Once it appears,
// it considers it a success.
/**
* Hover at a position until we see a preview element (popup, tooltip) appear.
* Note that this is using CodeMirror method to retrieve the token element
* and that could be subject to CodeMirror bugs / outdated internal state
*
* @param {Debugger} dbg
* @param {Integer} line: The line we want to hover over
* @param {Integer} column: The column we want to hover over
* @param {String} elementName: "Selector" string that will be passed to waitForElement,
* describing the element that should be displayed on hover.
* @returns Promise<{element, tokenEl}>
* element is the DOM element matching the passed elementName
* tokenEl is the DOM element for the token we hovered
*/
async function tryHovering(dbg, line, column, elementName) {
ok(
!findElement(dbg, elementName),
@ -2256,13 +2296,87 @@ async function tryHovering(dbg, line, column, elementName) {
);
const tokenEl = await getTokenFromPosition(dbg, { line, column });
return tryHoverToken(dbg, tokenEl, elementName);
}
/**
* Retrieve the token element matching `expression` at line `line` and hover it.
* This is retrieving the token from the DOM, contrary to `tryHovering`, which calls
* CodeMirror internal method for this (and which might suffer from bugs / outdated internal state)
*
* @param {Debugger} dbg
* @param {String} expression: The text of the token we want to hover
* @param {Integer} line: The line the token should be at
* @param {Integer} column: The column the token should be at
* @param {String} elementName: "Selector" string that will be passed to waitForElement,
* describing the element that should be displayed on hover.
* @returns Promise<{element, tokenEl}>
* element is the DOM element matching the passed elementName
* tokenEl is the DOM element for the token we hovered
*/
async function tryHoverTokenAtLine(dbg, expression, line, column, elementName) {
info("Scroll codeMirror to make the token visible");
const cm = getCM(dbg);
const onScrolled = waitForScrolling(cm);
cm.scrollIntoView({ line: line - 1, ch: 0 }, 0);
await onScrolled;
// Lookup for the token matching the passed expression
const tokenEl = getTokenElAtLine(dbg, expression, line, column);
if (!tokenEl) {
throw new Error(
`Couldn't find token <${expression}> on ${line}:${column}\n`
);
}
ok(true, `Found token <${expression}> on ${line}:${column}`);
return tryHoverToken(dbg, tokenEl, elementName);
}
async function tryHoverToken(dbg, tokenEl, elementName) {
hoverToken(tokenEl);
// Wait for the preview element to be created
const element = await waitForElement(dbg, elementName);
return { element, tokenEl };
}
/**
* Retrieve the token element matching `expression` at line `line`, from the DOM.
*
* @param {Debugger} dbg
* @param {String} expression: The text of the token we want to hover
* @param {Integer} line: The line the token should be at
* @param {Integer} column: The column the token should be at
* @returns {Element} the token element, or null if not found
*/
function getTokenElAtLine(dbg, expression, line, column = 0) {
info(`Search for <${expression}> token on ${line}:${column}`);
// Get the line gutter element matching the passed line
const lineGutterEl = [
...dbg.win.document.querySelectorAll(".CodeMirror-linenumber"),
].find(el => el.textContent === `${line}`);
// Get the related editor line
const editorLineEl = lineGutterEl
.closest(".CodeMirror-gutter-wrapper")
.parentElement.querySelector(".CodeMirror-line");
// Lookup for the token matching the passed expression
let currentColumn = 1;
return Array.from(editorLineEl.childNodes[0].childNodes).find(child => {
const childText = child.textContent;
currentColumn += childText.length;
// Only consider elements that are after the passed column
if (currentColumn < column) {
return false;
}
return childText === expression;
});
}
/**
* Hovers and asserts tooltip previews with simple text expressions (i.e numbers and strings)
* @param {*} dbg
@ -2279,21 +2393,17 @@ async function assertPreviewTextValue(
column,
{ result, expression, doNotClose = false }
) {
const { element: previewEl, tokenEl } = await tryHovering(
// CodeMirror refreshes after inline previews are displayed, so wait until they're rendered.
await waitForInlinePreviews(dbg);
const { element: previewEl, tokenEl } = await tryHoverTokenAtLine(
dbg,
expression,
line,
column,
"previewPopup"
);
ok(
tokenEl.innerText.includes(expression),
"Popup preview hovered expression is correct. Got: " +
tokenEl.innerText +
" Expected: " +
expression
);
ok(
previewEl.innerText.includes(result),
"Popup preview text shown to user. Got: " +
@ -2313,6 +2423,20 @@ async function assertPreviewTextValue(
* @param {Array} previews
*/
async function assertPreviews(dbg, previews) {
// Move the cursor to the top left corner to have a clean state
EventUtils.synthesizeMouse(
findElement(dbg, "codeMirror"),
0,
0,
{
type: "mousemove",
},
dbg.win
);
// CodeMirror refreshes after inline previews are displayed, so wait until they're rendered.
await waitForInlinePreviews(dbg);
for (const { line, column, expression, result, header, fields } of previews) {
info(" # Assert preview on " + line + ":" + column);
@ -2324,12 +2448,9 @@ async function assertPreviews(dbg, previews) {
}
if (fields) {
const { element: popupEl, tokenEl } = await tryHovering(
dbg,
line,
column,
"popup"
);
const { element: popupEl, tokenEl } = expression
? await tryHoverTokenAtLine(dbg, expression, line, column, "popup")
: await tryHovering(dbg, line, column, "popup");
info("Wait for child nodes to load");
await waitUntil(

View file

@ -33,8 +33,10 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
["browser_computed_default_tab.js"]
["browser_computed_getNodeInfo.js"]
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
skip-if = ["!debug && os == 'mac'"] #Bug 1559033
skip-if = [
"!debug && os == 'mac'", #Bug 1559033
"a11y_checks", # Bugs 1849028 and 1858041 to investigate intermittent a11y_checks results
]
["browser_computed_keybindings_01.js"]
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled

View file

@ -388,8 +388,6 @@ skip-if = ["os == 'win'"] # bug 1391264
["browser_net_reload-button.js"]
["browser_net_reload-markers.js"]
["browser_net_req-resp-bodies.js"]
["browser_net_resend.js"]

View file

@ -1,34 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if the empty-requests reload button works.
*/
add_task(async function () {
const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 });
info("Starting test... ");
const { document } = monitor.panelWin;
const markersDone = waitForTimelineMarkers(monitor);
const button = document.querySelector(".requests-list-reload-notice-button");
button.click();
await waitForNetworkEvents(monitor, 1);
const markers = await markersDone;
ok(true, "Reloading finished");
is(
markers[0].name,
"dom-interactive",
"The first received marker is correct."
);
is(markers[1].name, "dom-complete", "The second received marker is correct.");
return teardown(monitor);
});

View file

@ -47,7 +47,7 @@ skip-if = ["a11y_checks"] # Bug 1858041 and 1849028 intermittent a11y_checks res
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
["browser_device_custom_remove.js"]
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
skip-if = ["a11y_checks"] # Bug 1849028, 1858041 to investigate intermittent a11y_checks results (fails on Try, passes on Autoland)
["browser_device_modal_exit.js"]

View file

@ -25,7 +25,11 @@
}
.frames .frame {
display: contents;
/* Parent is a grid container whose grid template we want to use, so span the whole line */
grid-column: 1 / -1;
display: grid;
/* Grid is defined in `.frames [role="list"]` rule */
grid-template-columns: subgrid;
cursor: pointer;
white-space: normal;
}
@ -76,7 +80,6 @@
}
.frames .frames-group .frames-list {
grid-column: 1 / -1;
margin-block-start: 2px;
/*
* We want to display each frame name on its own row, without having new lines in the
@ -87,7 +90,7 @@
}
.frames .frames-group .frames-list .frame {
padding-inline-start: 16px;
padding-inline-start: 0;
text-overflow: ellipsis;
}

View file

@ -5,36 +5,68 @@
"use strict";
const TEST_URI = `data:text/html;charset=utf-8,<!DOCTYPE html><p>Test keyboard accessibility</p>
const HTML_FILENAME = `test.html`;
const HTML_CONTENT = `<!DOCTYPE html><p>Test keyboard accessibility</p>
<script>
for (let i = 1; i <= 100; i++) {
console.log("console message " + i);
}
function logTrace() {
const sub = () => console.trace("console trace message");
sub();
}
logTrace();
</script>
`;
const TRACE_FRAME_LINE_REGEX = /test\.html:\d+:?\d*/;
const httpServer = createTestHTTPServer();
httpServer.registerContentType("html", "text/html");
httpServer.registerPathHandler(
"/" + HTML_FILENAME,
function (request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(HTML_CONTENT);
}
);
const port = httpServer.identity.primaryPort;
const TEST_URI = `http://localhost:${port}/${HTML_FILENAME}`;
add_task(async function () {
// Force tabfocus for all elements on OSX.
SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] });
const hud = await openNewTabAndConsole(TEST_URI);
info("Web Console opened");
const outputScroller = hud.ui.outputScroller;
await waitFor(
() => findConsoleAPIMessage(hud, "console message 100"),
const traceMsgNode = await waitFor(
() => findConsoleAPIMessage(hud, "console trace message"),
"waiting for all the messages to be displayed",
100,
1000
);
// wait for all the stacktrace frames to be rendered.
await waitFor(() =>
traceMsgNode.querySelector(".message-body-wrapper > .stacktrace .frames")
);
let currentPosition = outputScroller.scrollTop;
const bottom = currentPosition;
hud.jsterm.focus();
// Page up.
info("Check Page up keyboard shortcut");
EventUtils.synthesizeKey("KEY_PageUp");
isnot(
outputScroller.scrollTop,
currentPosition,
"scroll position changed after page up"
);
// Page down.
info("Check Page down keyboard shortcut");
currentPosition = outputScroller.scrollTop;
EventUtils.synthesizeKey("KEY_PageDown");
ok(
@ -42,11 +74,11 @@ add_task(async function () {
"scroll position now at bottom"
);
// Home
info("Check Home keyboard shortcut");
EventUtils.synthesizeKey("KEY_Home");
is(outputScroller.scrollTop, 0, "scroll position now at top");
// End
info("Check End keyboard shortcut");
EventUtils.synthesizeKey("KEY_End");
const scrollTop = outputScroller.scrollTop;
ok(
@ -54,7 +86,113 @@ add_task(async function () {
"scroll position now at bottom"
);
// Clear output
info("Hit Shift-Tab to focus switch to editor mode button");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
ok(
outputScroller.ownerDocument.activeElement.classList.contains(
"webconsole-input-openEditorButton"
),
"switch to editor mode button is focused"
);
info("Check stacktrace frames can be focused");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
ok(
outputScroller.ownerDocument.activeElement.closest(".message.trace"),
"The active element is in the trace message"
);
is(
TRACE_FRAME_LINE_REGEX.exec(
outputScroller.ownerDocument.activeElement.innerText
)?.[0],
"test.html:10",
`last frame of the stacktrace is focused ${outputScroller.ownerDocument.activeElement.innerText}`
);
is(
outputScroller.ownerDocument.activeElement.getAttribute("class"),
"frame",
"active element has expected class"
);
info("Hit Tab to navigate to second frame of the stacktrace");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
ok(
outputScroller.ownerDocument.activeElement.closest(".message.trace"),
"The active element is in the trace message"
);
is(
TRACE_FRAME_LINE_REGEX.exec(
outputScroller.ownerDocument.activeElement.innerText
)?.[0],
"test.html:8",
"second frame of the stacktrace is focused"
);
is(
outputScroller.ownerDocument.activeElement.getAttribute("class"),
"frame",
"active element has expected class"
);
info("Hit Tab to navigate to first frame of the stacktrace");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
ok(
outputScroller.ownerDocument.activeElement.closest(".message.trace"),
"The active element is in the trace message"
);
is(
TRACE_FRAME_LINE_REGEX.exec(
outputScroller.ownerDocument.activeElement.innerText
)?.[0],
"test.html:7",
"third frame of the stacktrace is focused"
);
is(
outputScroller.ownerDocument.activeElement.getAttribute("class"),
"frame",
"active element has expected class"
);
info("Hit Tab to navigate to the message location");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
ok(
outputScroller.ownerDocument.activeElement.closest(".message.trace"),
"The active element is in the trace message"
);
is(
outputScroller.ownerDocument.activeElement.getAttribute("class"),
"frame-link-source",
"active element is the console trace message location"
);
is(
TRACE_FRAME_LINE_REGEX.exec(
outputScroller.ownerDocument.activeElement.innerText
)?.[0],
"test.html:7:33",
"active element is expected message location"
);
info("Hit Tab to navigate to the previous message location");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
ok(
!outputScroller.ownerDocument.activeElement.closest(".message.trace"),
"The active element is not in the trace message"
);
is(
outputScroller.ownerDocument.activeElement.getAttribute("class"),
"frame-link-source",
"active element is the console trace message location"
);
is(
TRACE_FRAME_LINE_REGEX.exec(
outputScroller.ownerDocument.activeElement.innerText
)[0],
"test.html:4:15",
"active element is expected message location"
);
info("Clear output");
hud.jsterm.focus();
info("try ctrl-l to clear output");
let clearShortcut;
if (Services.appinfo.OS === "Darwin") {

View file

@ -70,6 +70,21 @@ special-powers-sandbox
Defines the environment for scripts evaluated inside ``SpecialPowers`` sandbox
with the default options.
testharness
-----------
Defines the environment the globals that are injected from
:searchfox:`dom/imptests/testharness.js <dom/imptests/testharness.js>`.
It is injected automatically into (x)html files which include:
.. code-block:: html
<script src="/resources/testharness.js"></script>
It may need to be included manually in JavaScript files which are loaded into
the same scope.
xpcshell
--------

View file

@ -28,10 +28,6 @@ with Files("nsAboutRedirector.*"):
with Files("nsIScrollObserver.*"):
BUG_COMPONENT = ("Core", "Panning and Zooming")
DIRS += [
"timeline",
]
XPIDL_SOURCES += [
"nsIContentViewer.idl",
"nsIContentViewerEdit.idl",

View file

@ -30,7 +30,6 @@
#include "mozilla/LoadInfo.h"
#include "mozilla/Logging.h"
#include "mozilla/MediaFeatureChange.h"
#include "mozilla/ObservedDocShell.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/ResultExtensions.h"
@ -70,7 +69,6 @@
#include "mozilla/dom/PerformanceNavigation.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/PopupBlocker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "mozilla/dom/ScreenOrientation.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ServiceWorkerInterceptController.h"
@ -243,7 +241,6 @@
#include "mozpkix/pkix.h"
#include "NSSErrorsService.h"
#include "timeline/JavascriptTimelineMarker.h"
#include "nsDocShellTelemetryUtils.h"
#ifdef MOZ_PLACES
@ -335,7 +332,6 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
mLoadType(0),
mFailedLoadType(0),
mJSRunToCompletionDepth(0),
mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
mChannelToDisconnectOnPageHide(0),
mCreatingDocument(false),
@ -388,8 +384,6 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
}
nsDocShell::~nsDocShell() {
MOZ_ASSERT(!mObserved);
// Avoid notifying observers while we're in the dtor.
mIsBeingDestroyed = true;
@ -2238,49 +2232,6 @@ nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) {
bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
if (currentValue == aValue) {
return NS_OK;
}
if (aValue) {
MOZ_ASSERT(!TimelineConsumers::HasConsumer(this));
TimelineConsumers::AddConsumer(this);
MOZ_ASSERT(TimelineConsumers::HasConsumer(this));
UseEntryScriptProfiling();
} else {
MOZ_ASSERT(TimelineConsumers::HasConsumer(this));
TimelineConsumers::RemoveConsumer(this);
MOZ_ASSERT(!TimelineConsumers::HasConsumer(this));
UnuseEntryScriptProfiling();
}
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) {
*aValue = !!mObserved;
return NS_OK;
}
nsresult nsDocShell::PopProfileTimelineMarkers(
JSContext* aCx, JS::MutableHandle<JS::Value> aOut) {
nsTArray<dom::ProfileTimelineMarker> store;
SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
TimelineConsumers::PopMarkers(this, aCx, store);
if (!ToJSValue(aCx, store, aOut)) {
JS_ClearPendingException(aCx);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
*aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
return NS_OK;
@ -4527,9 +4478,6 @@ nsDocShell::Destroy() {
// Brak the cycle with the initial client, if present.
mInitialClientSource.reset();
// Make sure we don't record profile timeline markers anymore
SetRecordProfileTimelineMarkers(false);
// Make sure to blow away our mLoadingURI just in case. No loads
// from inside this pagehide.
mLoadingURI = nullptr;
@ -13482,34 +13430,6 @@ bool nsDocShell::IsInvisible() { return mInvisible; }
void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
// The caller owns |aAsyncCause| here.
void nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
const nsAString& aFunctionName,
const nsAString& aFilename,
const uint32_t aLineNumber,
JS::Handle<JS::Value> aAsyncStack,
const char* aAsyncCause) {
// If first start, mark interval start.
if (mJSRunToCompletionDepth == 0 && TimelineConsumers::HasConsumer(this)) {
TimelineConsumers::AddMarkerForDocShell(
this, mozilla::MakeUnique<JavascriptTimelineMarker>(
aReason, aFunctionName, aFilename, aLineNumber,
MarkerTracingType::START, aAsyncStack, aAsyncCause));
}
mJSRunToCompletionDepth++;
}
void nsDocShell::NotifyJSRunToCompletionStop() {
mJSRunToCompletionDepth--;
// If last stop, mark interval end.
if (mJSRunToCompletionDepth == 0 && TimelineConsumers::HasConsumer(this)) {
TimelineConsumers::AddMarkerForDocShell(this, "Javascript",
MarkerTracingType::END);
}
}
/* static */
void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
const nsString& aKeyword) {

View file

@ -12,7 +12,6 @@
#include "mozilla/Maybe.h"
#include "mozilla/NotNull.h"
#include "mozilla/ScrollbarPreferences.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/BrowsingContext.h"
@ -539,23 +538,6 @@ class nsDocShell final : public nsDocLoader,
friend class mozilla::net::DocumentLoadListener;
friend class nsGlobalWindowOuter;
// It is necessary to allow adding a timeline marker wherever a docshell
// instance is available. This operation happens frequently and needs to
// be very fast, so instead of using a Map or having to search for some
// docshell-specific markers storage, a pointer to an `ObservedDocShell` is
// is stored on docshells directly.
friend void mozilla::TimelineConsumers::AddConsumer(nsDocShell*);
friend void mozilla::TimelineConsumers::RemoveConsumer(nsDocShell*);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, const char*, MarkerTracingType, MarkerStackRequest);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, const char*, const TimeStamp&, MarkerTracingType,
MarkerStackRequest);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, UniquePtr<AbstractTimelineMarker>&&);
friend void mozilla::TimelineConsumers::PopMarkers(
nsDocShell*, JSContext*, nsTArray<dom::ProfileTimelineMarker>&);
nsDocShell(mozilla::dom::BrowsingContext* aBrowsingContext,
uint64_t aContentWindowID);
@ -1202,9 +1184,6 @@ class nsDocShell final : public nsDocLoader,
*/
nsCString mContentTypeHint;
// An observed docshell wrapper is created when recording markers is enabled.
mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
// mCurrentURI should be marked immutable on set if possible.
// Change mCurrentURI only through SetCurrentURIInternal method.
nsCOMPtr<nsIURI> mCurrentURI;
@ -1293,10 +1272,6 @@ class nsDocShell final : public nsDocLoader,
uint32_t mLoadType;
uint32_t mFailedLoadType;
// A depth count of how many times NotifyRunToCompletionStart
// has been called without a matching NotifyRunToCompletionStop.
uint32_t mJSRunToCompletionDepth;
// Whether or not handling of the <meta name="viewport"> tag is overridden.
// Possible values are defined as constants in nsIDocShell.idl.
MetaViewportOverride mMetaViewportOverride;

View file

@ -478,11 +478,6 @@ interface nsIDocShell : nsIDocShellTreeItem
out int32_t parentCharsetSource,
out nsIPrincipal parentCharsetPrincipal);
/**
* Whether the docShell records profile timeline markers at the moment
*/
[infallible] attribute boolean recordProfileTimelineMarkers;
/**
* Return a DOMHighResTimeStamp representing the number of
* milliseconds from an arbitrary point in time. The reference
@ -491,12 +486,6 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
DOMHighResTimeStamp now();
/**
* Returns and flushes the profile timeline markers gathered by the docShell
*/
[implicit_jscontext]
jsval popProfileTimelineMarkers();
/**
* Add an observer to the list of parties to be notified when this docshell's
* private browsing status is changed. |obs| must support weak references.
@ -622,19 +611,6 @@ interface nsIDocShell : nsIDocShellTreeItem
[noscript,notxpcom,nostdcall] Document getExtantDocument();
/**
* Notify DocShell when the browser is about to start executing JS, and after
* that execution has stopped. This only occurs when the Timeline devtool
* is collecting information.
*/
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStart(in string aReason,
in AString functionName,
in AString fileName,
in unsigned long lineNumber,
in jsval asyncStack,
in string asyncCause);
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStop();
/**
* This attribute determines whether a document which is not about:blank has
* already be loaded by this docShell.

View file

@ -1,72 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#include "AbstractTimelineMarker.h"
#include "mozilla/TimeStamp.h"
#include "MainThreadUtils.h"
#include "nsAppRunner.h"
namespace mozilla {
AbstractTimelineMarker::AbstractTimelineMarker(const char* aName,
MarkerTracingType aTracingType)
: mName(aName),
mTracingType(aTracingType),
mProcessType(XRE_GetProcessType()),
mIsOffMainThread(!NS_IsMainThread()) {
MOZ_COUNT_CTOR(AbstractTimelineMarker);
SetCurrentTime();
}
AbstractTimelineMarker::AbstractTimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType)
: mName(aName),
mTracingType(aTracingType),
mProcessType(XRE_GetProcessType()),
mIsOffMainThread(!NS_IsMainThread()) {
MOZ_COUNT_CTOR(AbstractTimelineMarker);
SetCustomTime(aTime);
}
UniquePtr<AbstractTimelineMarker> AbstractTimelineMarker::Clone() {
MOZ_ASSERT(false, "Clone method not yet implemented on this marker type.");
return nullptr;
}
bool AbstractTimelineMarker::Equals(const AbstractTimelineMarker& aOther) {
// Check whether two markers should be considered the same, for the purpose
// of pairing start and end markers. Normally this definition suffices.
return strcmp(mName, aOther.mName) == 0;
}
AbstractTimelineMarker::~AbstractTimelineMarker() {
MOZ_COUNT_DTOR(AbstractTimelineMarker);
}
void AbstractTimelineMarker::SetCurrentTime() {
TimeStamp now = TimeStamp::Now();
SetCustomTime(now);
}
void AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime) {
mTime = (aTime - TimeStamp::ProcessCreation()).ToMilliseconds();
}
void AbstractTimelineMarker::SetCustomTime(DOMHighResTimeStamp aTime) {
mTime = aTime;
}
void AbstractTimelineMarker::SetProcessType(GeckoProcessType aProcessType) {
mProcessType = aProcessType;
}
void AbstractTimelineMarker::SetOffMainThread(bool aIsOffMainThread) {
mIsOffMainThread = aIsOffMainThread;
}
} // namespace mozilla

View file

@ -1,71 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_AbstractTimelineMarker_h_
#define mozilla_AbstractTimelineMarker_h_
#include "TimelineMarkerEnums.h" // for MarkerTracingType
#include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp
#include "nsXULAppAPI.h" // for GeckoProcessType
#include "mozilla/UniquePtr.h"
struct JSContext;
class JSObject;
namespace mozilla {
class TimeStamp;
namespace dom {
struct ProfileTimelineMarker;
}
class AbstractTimelineMarker {
private:
AbstractTimelineMarker() = delete;
AbstractTimelineMarker(const AbstractTimelineMarker& aOther) = delete;
void operator=(const AbstractTimelineMarker& aOther) = delete;
public:
AbstractTimelineMarker(const char* aName, MarkerTracingType aTracingType);
AbstractTimelineMarker(const char* aName, const TimeStamp& aTime,
MarkerTracingType aTracingType);
virtual ~AbstractTimelineMarker();
virtual UniquePtr<AbstractTimelineMarker> Clone();
virtual bool Equals(const AbstractTimelineMarker& aOther);
virtual void AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) = 0;
virtual JSObject* GetStack() = 0;
const char* GetName() const { return mName; }
DOMHighResTimeStamp GetTime() const { return mTime; }
MarkerTracingType GetTracingType() const { return mTracingType; }
uint8_t GetProcessType() const { return mProcessType; };
bool IsOffMainThread() const { return mIsOffMainThread; };
private:
const char* mName;
DOMHighResTimeStamp mTime;
MarkerTracingType mTracingType;
uint8_t mProcessType; // @see `enum GeckoProcessType`.
bool mIsOffMainThread;
protected:
void SetCurrentTime();
void SetCustomTime(const TimeStamp& aTime);
void SetCustomTime(DOMHighResTimeStamp aTime);
void SetProcessType(GeckoProcessType aProcessType);
void SetOffMainThread(bool aIsOffMainThread);
};
} // namespace mozilla
#endif /* mozilla_AbstractTimelineMarker_h_ */

View file

@ -1,39 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#include "AutoGlobalTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
namespace mozilla {
AutoGlobalTimelineMarker::AutoGlobalTimelineMarker(
const char* aName, MarkerStackRequest aStackRequest /* = STACK */
)
: mName(aName), mStackRequest(aStackRequest) {
MOZ_ASSERT(NS_IsMainThread());
if (TimelineConsumers::IsEmpty()) {
return;
}
TimelineConsumers::AddMarkerForAllObservedDocShells(
mName, MarkerTracingType::START, mStackRequest);
}
AutoGlobalTimelineMarker::~AutoGlobalTimelineMarker() {
MOZ_ASSERT(NS_IsMainThread());
if (TimelineConsumers::IsEmpty()) {
return;
}
TimelineConsumers::AddMarkerForAllObservedDocShells(
mName, MarkerTracingType::END, mStackRequest);
}
} // namespace mozilla

View file

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_AutoGlobalTimelineMarker_h_
#define mozilla_AutoGlobalTimelineMarker_h_
#include "mozilla/Attributes.h"
#include "TimelineMarkerEnums.h"
namespace mozilla {
// # AutoGlobalTimelineMarker
//
// Similar to `AutoTimelineMarker`, but adds its traced marker to all docshells,
// not a single particular one. This is useful for operations that aren't
// associated with any one particular doc shell, or when it isn't clear which
// docshell triggered the operation.
//
// Example usage:
//
// {
// AutoGlobalTimelineMarker marker("Cycle Collection");
// nsCycleCollector* cc = GetCycleCollector();
// cc->Collect();
// ...
// }
class MOZ_RAII AutoGlobalTimelineMarker {
// The name of the marker we are adding.
const char* mName;
// Whether to capture the JS stack or not.
MarkerStackRequest mStackRequest;
public:
explicit AutoGlobalTimelineMarker(
const char* aName,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
~AutoGlobalTimelineMarker();
AutoGlobalTimelineMarker(const AutoGlobalTimelineMarker& aOther) = delete;
void operator=(const AutoGlobalTimelineMarker& aOther) = delete;
};
} // namespace mozilla
#endif /* mozilla_AutoGlobalTimelineMarker_h_ */

View file

@ -1,51 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#include "AutoRestyleTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
#include "nsIDocShell.h"
#include "RestyleTimelineMarker.h"
namespace mozilla {
AutoRestyleTimelineMarker::AutoRestyleTimelineMarker(nsIDocShell* aDocShell,
bool aIsAnimationOnly)
: mDocShell(nullptr), mIsAnimationOnly(aIsAnimationOnly) {
MOZ_ASSERT(NS_IsMainThread());
if (!aDocShell) {
return;
}
if (!TimelineConsumers::HasConsumer(aDocShell)) {
return;
}
mDocShell = aDocShell;
TimelineConsumers::AddMarkerForDocShell(
mDocShell, MakeUnique<RestyleTimelineMarker>(mIsAnimationOnly,
MarkerTracingType::START));
}
AutoRestyleTimelineMarker::~AutoRestyleTimelineMarker() {
MOZ_ASSERT(NS_IsMainThread());
if (!mDocShell) {
return;
}
if (!TimelineConsumers::HasConsumer(mDocShell)) {
return;
}
TimelineConsumers::AddMarkerForDocShell(
mDocShell, MakeUnique<RestyleTimelineMarker>(mIsAnimationOnly,
MarkerTracingType::END));
}
} // namespace mozilla

View file

@ -1,30 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_AutoRestyleTimelineMarker_h_
#define mozilla_AutoRestyleTimelineMarker_h_
#include "mozilla/RefPtr.h"
class nsIDocShell;
namespace mozilla {
class MOZ_RAII AutoRestyleTimelineMarker {
RefPtr<nsIDocShell> mDocShell;
bool mIsAnimationOnly;
public:
AutoRestyleTimelineMarker(nsIDocShell* aDocShell, bool aIsAnimationOnly);
~AutoRestyleTimelineMarker();
AutoRestyleTimelineMarker(const AutoRestyleTimelineMarker& aOther) = delete;
void operator=(const AutoRestyleTimelineMarker& aOther) = delete;
};
} // namespace mozilla
#endif /* mozilla_AutoRestyleTimelineMarker_h_ */

View file

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#include "AutoTimelineMarker.h"
#include "nsIDocShell.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
namespace mozilla {
AutoTimelineMarker::AutoTimelineMarker(nsIDocShell* aDocShell,
const char* aName)
: mName(aName), mDocShell(nullptr) {
MOZ_ASSERT(NS_IsMainThread());
if (!aDocShell) {
return;
}
if (!TimelineConsumers::HasConsumer(aDocShell)) {
return;
}
mDocShell = aDocShell;
TimelineConsumers::AddMarkerForDocShell(mDocShell, mName,
MarkerTracingType::START);
}
AutoTimelineMarker::~AutoTimelineMarker() {
MOZ_ASSERT(NS_IsMainThread());
if (!mDocShell) {
return;
}
if (!TimelineConsumers::HasConsumer(mDocShell)) {
return;
}
TimelineConsumers::AddMarkerForDocShell(mDocShell, mName,
MarkerTracingType::END);
}
} // namespace mozilla

View file

@ -1,46 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_AutoTimelineMarker_h_
#define mozilla_AutoTimelineMarker_h_
#include "mozilla/RefPtr.h"
class nsIDocShell;
namespace mozilla {
// # AutoTimelineMarker
//
// An RAII class to trace some task in the platform by adding a start and end
// timeline marker pair. These markers are then rendered in the devtools'
// performance tool's waterfall graph.
//
// Example usage:
//
// {
// AutoTimelineMarker marker(mDocShell, "Parse CSS");
// nsresult rv = ParseTheCSSFile(mFile);
// ...
// }
class MOZ_RAII AutoTimelineMarker {
// The name of the marker we are adding.
const char* mName;
// The docshell that is associated with this marker.
RefPtr<nsIDocShell> mDocShell;
public:
AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName);
~AutoTimelineMarker();
AutoTimelineMarker(const AutoTimelineMarker& aOther) = delete;
void operator=(const AutoTimelineMarker& aOther) = delete;
};
} // namespace mozilla
#endif /* mozilla_AutoTimelineMarker_h_ */

View file

@ -1,31 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_CompositeTimelineMarker_h_
#define mozilla_CompositeTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class CompositeTimelineMarker : public TimelineMarker {
public:
CompositeTimelineMarker(const TimeStamp& aTime,
MarkerTracingType aTracingType)
: TimelineMarker("Composite", aTime, aTracingType) {
// Even though these markers end up being created on the main thread in the
// content or chrome processes, they actually trace down code in the
// compositor parent process. All the information for creating these markers
// is sent along via IPC to an nsView when a composite finishes.
// Mark this as 'off the main thread' to style it differently in frontends.
SetOffMainThread(true);
}
};
} // namespace mozilla
#endif // mozilla_CompositeTimelineMarker_h_

View file

@ -1,53 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_ConsoleTimelineMarker_h_
#define mozilla_ConsoleTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class ConsoleTimelineMarker : public TimelineMarker {
public:
ConsoleTimelineMarker(const nsAString& aCause, MarkerTracingType aTracingType)
: TimelineMarker("ConsoleTime", aTracingType), mCause(aCause) {
// Stack is captured by default on the "start" marker. Explicitly also
// capture stack on the "end" marker.
if (aTracingType == MarkerTracingType::END) {
CaptureStack();
}
}
virtual bool Equals(const AbstractTimelineMarker& aOther) override {
if (!TimelineMarker::Equals(aOther)) {
return false;
}
// Console markers must have matching causes as well. It is safe to perform
// a static_cast here as the previous equality check ensures that this is
// a console marker instance.
return mCause == static_cast<const ConsoleTimelineMarker*>(&aOther)->mCause;
}
virtual void AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override {
TimelineMarker::AddDetails(aCx, aMarker);
if (GetTracingType() == MarkerTracingType::START) {
aMarker.mCauseName.Construct(mCause);
} else {
aMarker.mEndStack = GetStack();
}
}
private:
nsString mCause;
};
} // namespace mozilla
#endif // mozilla_ConsoleTimelineMarker_h_

View file

@ -1,38 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_DocLoadingTimelineMarker_h_
#define mozilla_DocLoadingTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class DocLoadingTimelineMarker : public TimelineMarker {
public:
explicit DocLoadingTimelineMarker(const char* aName)
: TimelineMarker(aName, MarkerTracingType::TIMESTAMP),
mUnixTime(PR_Now()) {}
virtual void AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override {
TimelineMarker::AddDetails(aCx, aMarker);
aMarker.mUnixTime.Construct(mUnixTime);
}
private:
// Certain consumers might use Date.now() or similar for tracing time.
// However, TimelineMarkers use process creation as an epoch, which provides
// more precision. To allow syncing, attach an additional unix timestamp.
// Using this instead of `AbstractTimelineMarker::GetTime()'s` timestamp
// is strongly discouraged.
PRTime mUnixTime;
};
} // namespace mozilla
#endif // mozilla_DocLoadingTimelineMarker_h_

View file

@ -1,40 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_EventTimelineMarker_h_
#define mozilla_EventTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class EventTimelineMarker : public TimelineMarker {
public:
EventTimelineMarker(const nsAString& aType, uint16_t aPhase,
MarkerTracingType aTracingType)
: TimelineMarker("DOMEvent", aTracingType),
mType(aType),
mPhase(aPhase) {}
virtual void AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override {
TimelineMarker::AddDetails(aCx, aMarker);
if (GetTracingType() == MarkerTracingType::START) {
aMarker.mType.Construct(mType);
aMarker.mEventPhase.Construct(mPhase);
}
}
private:
nsString mType;
uint16_t mPhase;
};
} // namespace mozilla
#endif // mozilla_EventTimelineMarker_h_

View file

@ -1,96 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_JavascriptTimelineMarker_h_
#define mozilla_JavascriptTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/Maybe.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/ToJSValue.h"
namespace mozilla {
class JavascriptTimelineMarker : public TimelineMarker {
public:
// The caller owns |aAsyncCause| here, so we must copy it into a separate
// string for use later on.
JavascriptTimelineMarker(const char* aReason, const nsAString& aFunctionName,
const nsAString& aFileName, uint32_t aLineNumber,
MarkerTracingType aTracingType,
JS::Handle<JS::Value> aAsyncStack,
const char* aAsyncCause)
: TimelineMarker("Javascript", aTracingType,
MarkerStackRequest::NO_STACK),
mCause(NS_ConvertUTF8toUTF16(aReason)),
mFunctionName(aFunctionName),
mFileName(aFileName),
mLineNumber(aLineNumber),
mAsyncCause(aAsyncCause) {
JSContext* ctx = nsContentUtils::GetCurrentJSContext();
if (ctx) {
mAsyncStack.init(ctx, aAsyncStack);
}
}
virtual void AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override {
TimelineMarker::AddDetails(aCx, aMarker);
aMarker.mCauseName.Construct(mCause);
if (!mFunctionName.IsEmpty() || !mFileName.IsEmpty()) {
dom::RootedDictionary<dom::ProfileTimelineStackFrame> stackFrame(aCx);
stackFrame.mLine.Construct(mLineNumber);
stackFrame.mSource.Construct(mFileName);
stackFrame.mFunctionDisplayName.Construct(mFunctionName);
if (mAsyncStack.isObject() && !mAsyncCause.IsEmpty()) {
JS::Rooted<JSObject*> asyncStack(aCx, &mAsyncStack.toObject());
JS::Rooted<JSObject*> parentFrame(aCx);
JS::Rooted<JSString*> asyncCause(
aCx, JS_NewUCStringCopyN(aCx, mAsyncCause.BeginReading(),
mAsyncCause.Length()));
if (!asyncCause) {
JS_ClearPendingException(aCx);
return;
}
if (JS::IsMaybeWrappedSavedFrame(asyncStack) &&
!JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame,
mozilla::Nothing())) {
JS_ClearPendingException(aCx);
} else {
stackFrame.mAsyncParent = parentFrame;
}
}
JS::Rooted<JS::Value> newStack(aCx);
if (ToJSValue(aCx, stackFrame, &newStack)) {
if (newStack.isObject()) {
aMarker.mStack = &newStack.toObject();
}
} else {
JS_ClearPendingException(aCx);
}
}
}
private:
nsString mCause;
nsString mFunctionName;
nsString mFileName;
uint32_t mLineNumber;
JS::PersistentRooted<JS::Value> mAsyncStack;
NS_ConvertUTF8toUTF16 mAsyncCause;
};
} // namespace mozilla
#endif // mozilla_JavascriptTimelineMarker_h_

View file

@ -1,47 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_LayerTimelineMarker_h_
#define mozilla_LayerTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "mozilla/mozalloc_oom.h"
#include "nsRegion.h"
namespace mozilla {
class LayerTimelineMarker : public TimelineMarker {
public:
explicit LayerTimelineMarker(const nsIntRegion& aRegion)
: TimelineMarker("Layer", MarkerTracingType::HELPER_EVENT),
mRegion(aRegion) {}
void AddLayerRectangles(
dom::Sequence<dom::ProfileTimelineLayerRect>& aRectangles) {
for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
const nsIntRect& iterRect = iter.Get();
dom::ProfileTimelineLayerRect rect;
rect.mX = iterRect.X();
rect.mY = iterRect.Y();
rect.mWidth = iterRect.Width();
rect.mHeight = iterRect.Height();
if (!aRectangles.AppendElement(rect, fallible)) {
// XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
// involve multiple reallocations) and potentially crashing here,
// SetCapacity could be called outside the loop once.
mozalloc_handle_oom(0);
}
}
}
private:
nsIntRegion mRegion;
};
} // namespace mozilla
#endif // mozilla_LayerTimelineMarker_h_

View file

@ -1,40 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_MarkersStorage_h_
#define mozilla_MarkersStorage_h_
#include "TimelineMarkerEnums.h" // for MarkerReleaseRequest
#include "MainThreadUtils.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/LinkedList.h"
#include "nsTArray.h"
namespace mozilla {
class AbstractTimelineMarker;
namespace dom {
struct ProfileTimelineMarker;
}
class MarkersStorage : public LinkedListElement<MarkersStorage> {
public:
MarkersStorage() { MOZ_ASSERT(NS_IsMainThread()); }
virtual ~MarkersStorage() { MOZ_ASSERT(NS_IsMainThread()); }
MarkersStorage(const MarkersStorage& aOther) = delete;
void operator=(const MarkersStorage& aOther) = delete;
virtual void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
virtual void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
virtual void ClearMarkers() = 0;
virtual void PopMarkers(JSContext* aCx,
nsTArray<dom::ProfileTimelineMarker>& aStore) = 0;
};
} // namespace mozilla
#endif /* mozilla_MarkersStorage_h_ */

View file

@ -1,46 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_MessagePortTimelineMarker_h_
#define mozilla_MessagePortTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class MessagePortTimelineMarker : public TimelineMarker {
public:
MessagePortTimelineMarker(
dom::ProfileTimelineMessagePortOperationType aOperationType,
MarkerTracingType aTracingType)
: TimelineMarker("MessagePort", aTracingType,
MarkerStackRequest::NO_STACK),
mOperationType(aOperationType) {}
virtual UniquePtr<AbstractTimelineMarker> Clone() override {
MessagePortTimelineMarker* clone =
new MessagePortTimelineMarker(mOperationType, GetTracingType());
clone->SetCustomTime(GetTime());
return UniquePtr<AbstractTimelineMarker>(clone);
}
virtual void AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override {
TimelineMarker::AddDetails(aCx, aMarker);
if (GetTracingType() == MarkerTracingType::START) {
aMarker.mMessagePortOperation.Construct(mOperationType);
}
}
private:
dom::ProfileTimelineMessagePortOperationType mOperationType;
};
} // namespace mozilla
#endif /* mozilla_MessagePortTimelineMarker_h_ */

View file

@ -1,171 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#include "ObservedDocShell.h"
#include <utility>
#include "AbstractTimelineMarker.h"
#include "LayerTimelineMarker.h"
#include "MainThreadUtils.h"
#include "mozilla/AutoRestore.h"
#include "nsIDocShell.h"
namespace mozilla {
ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
: mDocShell(aDocShell), mLock("ObservedDocShellMutex") {
MOZ_ASSERT(NS_IsMainThread());
}
void ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) {
// Only allow main thread markers to go into this list. No need to lock
// here since `mTimelineMarkers` will only be accessed or modified on the
// main thread only.
MOZ_ASSERT(NS_IsMainThread());
// Don't accept any markers generated by the process of popping
// markers.
if (!mPopping) {
mTimelineMarkers.AppendElement(std::move(aMarker));
}
}
void ObservedDocShell::AddOTMTMarker(
UniquePtr<AbstractTimelineMarker>&& aMarker) {
// Only allow off the main thread markers to go into this list. Since most
// of our markers come from the main thread, be a little more efficient and
// avoid dealing with multithreading scenarios until all the markers are
// actually cleared or popped in `ClearMarkers` or `PopMarkers`.
MOZ_ASSERT(!NS_IsMainThread());
MutexAutoLock lock(mLock); // for `mOffTheMainThreadTimelineMarkers`.
mOffTheMainThreadTimelineMarkers.AppendElement(std::move(aMarker));
}
void ObservedDocShell::ClearMarkers() {
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mLock); // for `mOffTheMainThreadTimelineMarkers`.
mTimelineMarkers.Clear();
mOffTheMainThreadTimelineMarkers.Clear();
}
void ObservedDocShell::PopMarkers(
JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!mPopping);
AutoRestore<bool> resetPopping(mPopping);
mPopping = true;
{
MutexAutoLock lock(mLock); // for `mOffTheMainThreadTimelineMarkers`.
// First, move all of our markers into a single array. We'll chose
// the `mTimelineMarkers` store because that's where we expect most of
// our markers to be, and we can access it without holding the lock.
mTimelineMarkers.AppendElements(
std::move(mOffTheMainThreadTimelineMarkers));
}
// If we see an unpaired START, we keep it around for the next call
// to ObservedDocShell::PopMarkers. We store the kept START objects here.
nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;
for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
UniquePtr<AbstractTimelineMarker>& startPayload =
mTimelineMarkers.ElementAt(i);
// If this is a TIMESTAMP marker, there's no corresponding END,
// as it's a single unit of time, not a duration.
if (startPayload->GetTracingType() == MarkerTracingType::TIMESTAMP) {
dom::ProfileTimelineMarker* marker = aStore.AppendElement();
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
marker->mStart = startPayload->GetTime();
marker->mEnd = startPayload->GetTime();
marker->mStack = startPayload->GetStack();
startPayload->AddDetails(aCx, *marker);
continue;
}
// Whenever a START marker is found, look for the corresponding END
// and build a {name,start,end} JS object.
if (startPayload->GetTracingType() == MarkerTracingType::START) {
bool hasSeenEnd = false;
// "Paint" markers are different because painting is handled at root
// docshell level. The information that a paint was done is stored at
// sub-docshell level, but we can only be sure that a paint did actually
// happen in if a "Layer" marker was recorded too.
bool startIsPaintType = strcmp(startPayload->GetName(), "Paint") == 0;
bool hasSeenLayerType = false;
// If we are processing a "Paint" marker, we append information from
// all the embedded "Layer" markers to this array.
dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles;
// DOM events can be nested, so we must take care when searching
// for the matching end. It doesn't hurt to apply this logic to
// all event types.
uint32_t markerDepth = 0;
// The assumption is that the devtools timeline flushes markers frequently
// enough for the amount of markers to always be small enough that the
// nested for loop isn't going to be a performance problem.
for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
UniquePtr<AbstractTimelineMarker>& endPayload =
mTimelineMarkers.ElementAt(j);
bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
// Look for "Layer" markers to stream out "Paint" markers.
if (startIsPaintType && endIsLayerType) {
AbstractTimelineMarker* raw = endPayload.get();
LayerTimelineMarker* layerPayload =
static_cast<LayerTimelineMarker*>(raw);
layerPayload->AddLayerRectangles(layerRectangles);
hasSeenLayerType = true;
}
if (!startPayload->Equals(*endPayload)) {
continue;
}
if (endPayload->GetTracingType() == MarkerTracingType::START) {
++markerDepth;
continue;
}
if (endPayload->GetTracingType() == MarkerTracingType::END) {
if (markerDepth > 0) {
--markerDepth;
continue;
}
if (!startIsPaintType || (startIsPaintType && hasSeenLayerType)) {
dom::ProfileTimelineMarker* marker = aStore.AppendElement();
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
marker->mStart = startPayload->GetTime();
marker->mEnd = endPayload->GetTime();
marker->mStack = startPayload->GetStack();
if (hasSeenLayerType) {
marker->mRectangles.Construct(layerRectangles);
}
startPayload->AddDetails(aCx, *marker);
endPayload->AddDetails(aCx, *marker);
}
hasSeenEnd = true;
break;
}
}
// If we did not see the corresponding END, keep the START.
if (!hasSeenEnd) {
keptStartMarkers.AppendElement(
std::move(mTimelineMarkers.ElementAt(i)));
mTimelineMarkers.RemoveElementAt(i);
--i;
}
}
}
mTimelineMarkers = std::move(keptStartMarkers);
}
} // namespace mozilla

View file

@ -1,55 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_ObservedDocShell_h_
#define mozilla_ObservedDocShell_h_
#include "MarkersStorage.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Mutex.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
class nsIDocShell;
namespace mozilla {
class AbstractTimelineMarker;
namespace dom {
struct ProfileTimelineMarker;
}
// # ObservedDocShell
//
// A wrapper around a docshell for which docshell-specific markers are
// allowed to exist. See TimelineConsumers for register/unregister logic.
class ObservedDocShell : public MarkersStorage {
private:
RefPtr<nsIDocShell> mDocShell;
// Main thread only.
nsTArray<UniquePtr<AbstractTimelineMarker>> mTimelineMarkers;
bool mPopping = false;
// Off the main thread only.
Mutex mLock;
nsTArray<UniquePtr<AbstractTimelineMarker>> mOffTheMainThreadTimelineMarkers
MOZ_GUARDED_BY(mLock);
public:
explicit ObservedDocShell(nsIDocShell* aDocShell);
nsIDocShell* operator*() const { return mDocShell.get(); }
void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
void ClearMarkers() override;
void PopMarkers(JSContext* aCx,
nsTArray<dom::ProfileTimelineMarker>& aStore) override;
};
} // namespace mozilla
#endif /* mozilla_ObservedDocShell_h_ */

View file

@ -1,37 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_RestyleTimelineMarker_h_
#define mozilla_RestyleTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class RestyleTimelineMarker : public TimelineMarker {
public:
RestyleTimelineMarker(bool aIsAnimationOnly, MarkerTracingType aTracingType)
: TimelineMarker("Styles", aTracingType) {
mIsAnimationOnly = aIsAnimationOnly;
}
virtual void AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override {
TimelineMarker::AddDetails(aCx, aMarker);
if (GetTracingType() == MarkerTracingType::START) {
aMarker.mIsAnimationOnly.Construct(mIsAnimationOnly);
}
}
private:
bool mIsAnimationOnly;
};
} // namespace mozilla
#endif // mozilla_RestyleTimelineMarker_h_

View file

@ -1,202 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#include "TimelineConsumers.h"
#include "mozilla/ObservedDocShell.h"
#include "mozilla/TimelineMarker.h"
#include "jsapi.h"
#include "nsAppRunner.h" // for XRE_IsContentProcess, XRE_IsParentProcess
#include "nsCRT.h"
#include "nsDocShell.h"
namespace mozilla {
StaticMutex TimelineConsumers::sMutex;
uint32_t TimelineConsumers::sActiveConsumers = 0;
StaticAutoPtr<LinkedList<MarkersStorage>> TimelineConsumers::sMarkersStores;
LinkedList<MarkersStorage>& TimelineConsumers::MarkersStores() {
if (!sMarkersStores) {
sMarkersStores = new LinkedList<MarkersStorage>;
}
return *sMarkersStores;
}
void TimelineConsumers::AddConsumer(nsDocShell* aDocShell) {
MOZ_ASSERT(NS_IsMainThread());
StaticMutexAutoLock lock(
sMutex); // for `sActiveConsumers` and `sMarkersStores`.
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
MOZ_ASSERT(!observed);
if (sActiveConsumers == 0) {
JS::SetProfileTimelineRecordingEnabled(true);
}
sActiveConsumers++;
ObservedDocShell* obsDocShell = new ObservedDocShell(aDocShell);
MarkersStorage* storage = static_cast<MarkersStorage*>(obsDocShell);
observed.reset(obsDocShell);
MarkersStores().insertFront(storage);
}
void TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell) {
MOZ_ASSERT(NS_IsMainThread());
StaticMutexAutoLock lock(
sMutex); // for `sActiveConsumers` and `sMarkersStores`.
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
MOZ_ASSERT(observed);
sActiveConsumers--;
if (sActiveConsumers == 0) {
JS::SetProfileTimelineRecordingEnabled(false);
}
// Clear all markers from the `mTimelineMarkers` store.
observed->ClearMarkers();
// Remove self from the `sMarkersStores` store.
observed->remove();
// Prepare for becoming a consumer later.
observed.reset(nullptr);
}
bool TimelineConsumers::HasConsumer(nsIDocShell* aDocShell) {
MOZ_ASSERT(NS_IsMainThread());
return aDocShell ? aDocShell->GetRecordProfileTimelineMarkers() : false;
}
bool TimelineConsumers::IsEmpty() {
StaticMutexAutoLock lock(sMutex); // for `sActiveConsumers`.
return sActiveConsumers == 0;
}
void TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest) {
MOZ_ASSERT(NS_IsMainThread());
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(
MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest));
}
}
void TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest) {
MOZ_ASSERT(NS_IsMainThread());
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(
MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest));
}
}
void TimelineConsumers::AddMarkerForDocShell(
nsDocShell* aDocShell, UniquePtr<AbstractTimelineMarker>&& aMarker) {
MOZ_ASSERT(NS_IsMainThread());
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(std::move(aMarker));
}
}
void TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest) {
MOZ_ASSERT(NS_IsMainThread());
AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTracingType,
aStackRequest);
}
void TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest) {
MOZ_ASSERT(NS_IsMainThread());
AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTime,
aTracingType, aStackRequest);
}
void TimelineConsumers::AddMarkerForDocShell(
nsIDocShell* aDocShell, UniquePtr<AbstractTimelineMarker>&& aMarker) {
MOZ_ASSERT(NS_IsMainThread());
AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), std::move(aMarker));
}
void TimelineConsumers::AddMarkerForAllObservedDocShells(
const char* aName, MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest /* = STACK */) {
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `sMarkersStores`.
for (MarkersStorage* storage = MarkersStores().getFirst(); storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> marker =
MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest);
if (isMainThread) {
storage->AddMarker(std::move(marker));
} else {
storage->AddOTMTMarker(std::move(marker));
}
}
}
void TimelineConsumers::AddMarkerForAllObservedDocShells(
const char* aName, const TimeStamp& aTime, MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest /* = STACK */) {
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `sMarkersStores`.
for (MarkersStorage* storage = MarkersStores().getFirst(); storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> marker =
MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest);
if (isMainThread) {
storage->AddMarker(std::move(marker));
} else {
storage->AddOTMTMarker(std::move(marker));
}
}
}
void TimelineConsumers::AddMarkerForAllObservedDocShells(
UniquePtr<AbstractTimelineMarker>& aMarker) {
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `sMarkersStores`.
for (MarkersStorage* storage = MarkersStores().getFirst(); storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> clone = aMarker->Clone();
if (isMainThread) {
storage->AddMarker(std::move(clone));
} else {
storage->AddOTMTMarker(std::move(clone));
}
}
}
void TimelineConsumers::PopMarkers(
nsDocShell* aDocShell, JSContext* aCx,
nsTArray<dom::ProfileTimelineMarker>& aStore) {
MOZ_ASSERT(NS_IsMainThread());
if (!aDocShell || !aDocShell->mObserved) {
return;
}
aDocShell->mObserved->PopMarkers(aCx, aStore);
}
} // namespace mozilla

View file

@ -1,113 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_TimelineConsumers_h_
#define mozilla_TimelineConsumers_h_
#include "nsIObserver.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/LinkedList.h"
#include "mozilla/StaticMutex.h"
#include "nsTArray.h"
#include "TimelineMarkerEnums.h" // for MarkerTracingType
class nsDocShell;
class nsIDocShell;
struct JSContext;
namespace mozilla {
class TimeStamp;
class MarkersStorage;
class AbstractTimelineMarker;
namespace dom {
struct ProfileTimelineMarker;
}
class TimelineConsumers {
public:
// Methods for registering interested consumers (i.e. "devtools toolboxes").
// Each consumer should be directly focused on a particular docshell, but
// timeline markers don't necessarily have to be tied to that docshell.
// See the public `AddMarker*` methods below.
// Main thread only.
static void AddConsumer(nsDocShell* aDocShell);
static void RemoveConsumer(nsDocShell* aDocShell);
static bool HasConsumer(nsIDocShell* aDocShell);
// Checks if there's any existing interested consumer.
// May be called from any thread.
static bool IsEmpty();
// Methods for adding markers relevant for particular docshells, or generic
// (meaning that they either can't be tied to a particular docshell, or one
// wasn't accessible in the part of the codebase where they're instantiated).
// These will only add markers if at least one docshell is currently being
// observed by a timeline. Markers tied to a particular docshell won't be
// created unless that docshell is specifically being currently observed.
// See nsIDocShell::recordProfileTimelineMarkers
// These methods create a basic TimelineMarker from a name and some metadata,
// relevant for a specific docshell.
// Main thread only.
static void AddMarkerForDocShell(
nsDocShell* aDocShell, const char* aName, MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
static void AddMarkerForDocShell(
nsIDocShell* aDocShell, const char* aName, MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
static void AddMarkerForDocShell(
nsDocShell* aDocShell, const char* aName, const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
static void AddMarkerForDocShell(
nsIDocShell* aDocShell, const char* aName, const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
// These methods register and receive ownership of an already created marker,
// relevant for a specific docshell.
// Main thread only.
static void AddMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
static void AddMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
// These methods create a basic marker from a name and some metadata,
// which doesn't have to be relevant to a specific docshell.
// May be called from any thread.
static void AddMarkerForAllObservedDocShells(
const char* aName, MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
static void AddMarkerForAllObservedDocShells(
const char* aName, const TimeStamp& aTime, MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
// This method clones and registers an already instantiated marker,
// which doesn't have to be relevant to a specific docshell.
// May be called from any thread.
static void AddMarkerForAllObservedDocShells(
UniquePtr<AbstractTimelineMarker>& aMarker);
static void PopMarkers(nsDocShell* aDocShell, JSContext* aCx,
nsTArray<dom::ProfileTimelineMarker>& aStore);
private:
static StaticMutex sMutex;
static LinkedList<MarkersStorage>& MarkersStores() MOZ_REQUIRES(sMutex);
static uint32_t sActiveConsumers MOZ_GUARDED_BY(sMutex);
static StaticAutoPtr<LinkedList<MarkersStorage>> sMarkersStores
MOZ_GUARDED_BY(sMutex);
};
} // namespace mozilla
#endif /* mozilla_TimelineConsumers_h_ */

View file

@ -1,66 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#include "TimelineMarker.h"
#include "jsapi.h"
#include "js/Exception.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "nsContentUtils.h"
namespace mozilla {
TimelineMarker::TimelineMarker(const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest)
: AbstractTimelineMarker(aName, aTracingType) {
CaptureStackIfNecessary(aTracingType, aStackRequest);
}
TimelineMarker::TimelineMarker(const char* aName, const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest)
: AbstractTimelineMarker(aName, aTime, aTracingType) {
CaptureStackIfNecessary(aTracingType, aStackRequest);
}
void TimelineMarker::AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) {
if (GetTracingType() == MarkerTracingType::START) {
aMarker.mProcessType.Construct(GetProcessType());
aMarker.mIsOffMainThread.Construct(IsOffMainThread());
}
}
JSObject* TimelineMarker::GetStack() {
if (mStackTrace.initialized()) {
return mStackTrace;
}
return nullptr;
}
void TimelineMarker::CaptureStack() {
JSContext* ctx = nsContentUtils::GetCurrentJSContext();
if (ctx) {
JS::Rooted<JSObject*> stack(ctx);
if (JS::CaptureCurrentStack(ctx, &stack)) {
mStackTrace.init(ctx, stack.get());
} else {
JS_ClearPendingException(ctx);
}
}
}
void TimelineMarker::CaptureStackIfNecessary(MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest) {
if ((aTracingType == MarkerTracingType::START ||
aTracingType == MarkerTracingType::TIMESTAMP) &&
aStackRequest != MarkerStackRequest::NO_STACK) {
CaptureStack();
}
}
} // namespace mozilla

Some files were not shown because too many files have changed in this diff Show more