forked from mirrors/gecko-dev
Merge autoland to mozilla-central. a=merge
This commit is contained in:
commit
10d0e01455
380 changed files with 5397 additions and 7793 deletions
|
|
@ -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/**",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
@ -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>
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.`
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
You’ll 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 product’s 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 =
|
||||
|
|
|
|||
|
|
@ -519,7 +519,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "0c61d58a873eb41ad722d87dcdbe9c2ab60f3da0"
|
||||
"revision": "9aa18cd3d907df422ac066deb7b6c2cdc2bc19e8"
|
||||
},
|
||||
"es-MX": {
|
||||
"pin": false,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
],
|
||||
[
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
import React from "react";
|
||||
|
||||
export default function FrameIndent() {
|
||||
export default function FrameIndent({ indentLevel = 1 } = {}) {
|
||||
// \xA0 represents the non breakable space
|
||||
const nonBreakableSpaces = "\xA0\xA0\xA0\xA0";
|
||||
const indentWidth = 4 * indentLevel;
|
||||
const nonBreakableSpaces = "\xA0".repeat(indentWidth);
|
||||
return React.createElement(
|
||||
"span",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
);
|
||||
}, [])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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]}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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'",
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ add_task(async function () {
|
|||
{
|
||||
line: 50,
|
||||
column: 47,
|
||||
expression: "Foo.#privateStatic",
|
||||
expression: "#privateStatic",
|
||||
fields: [
|
||||
["first", `"a"`],
|
||||
["second", `"b"`],
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,6 @@ with Files("nsAboutRedirector.*"):
|
|||
with Files("nsIScrollObserver.*"):
|
||||
BUG_COMPONENT = ("Core", "Panning and Zooming")
|
||||
|
||||
DIRS += [
|
||||
"timeline",
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
"nsIContentViewer.idl",
|
||||
"nsIContentViewerEdit.idl",
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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
Loading…
Reference in a new issue