mirror of
https://github.com/Feodor2/Mypal68.git
synced 2025-06-18 06:45:44 -04:00
68.14.3 - everything else
This commit is contained in:
parent
2c2b226a2c
commit
48cfaacdb7
@ -63,10 +63,6 @@ devtools/client/shared/sourceeditor/codemirror/.*
|
||||
devtools/client/shared/sourceeditor/tern/.*
|
||||
dom/canvas/test/webgl-conf/checkout/closure-library/.*
|
||||
dom/media/gmp/rlz/.*
|
||||
dom/media/gmp/widevine-adapter/content_decryption_module.h
|
||||
dom/media/gmp/widevine-adapter/content_decryption_module_export.h
|
||||
dom/media/gmp/widevine-adapter/content_decryption_module_ext.h
|
||||
dom/media/gmp/widevine-adapter/content_decryption_module_proxy.h
|
||||
dom/media/platforms/ffmpeg/ffmpeg57/.*
|
||||
dom/media/platforms/ffmpeg/ffmpeg58/.*
|
||||
dom/media/platforms/ffmpeg/libav53/.*
|
||||
@ -111,7 +107,6 @@ js/src/vtune/jitprofiling.c
|
||||
js/src/vtune/jitprofiling.h
|
||||
js/src/vtune/legacy/.*
|
||||
media/ffvpx/.*
|
||||
media/gmp-clearkey/0.1/openaes/.*
|
||||
media/kiss_fft/.*
|
||||
media/libaom/.*
|
||||
media/libcubeb/.*
|
||||
|
@ -210,7 +210,6 @@ layout/mathml/imptests/
|
||||
# Exclude everything but self-hosted JS
|
||||
js/ductwork/
|
||||
js/examples/
|
||||
js/ipc/
|
||||
js/public/
|
||||
js/xpconnect/
|
||||
js/src/devtools/
|
||||
|
@ -71,13 +71,6 @@ module.exports = {
|
||||
"env": {
|
||||
"mozilla/browser-window": true
|
||||
}
|
||||
}, {
|
||||
// TODO: Bug 1513639. Temporarily turn off reject-importGlobalProperties
|
||||
// due to other ESLint enabling happening in DOM.
|
||||
"files": "dom/**",
|
||||
"rules": {
|
||||
"mozilla/reject-importGlobalProperties": "off",
|
||||
}
|
||||
}, {
|
||||
// TODO: Bug 1515949. Enable no-undef for gfx/
|
||||
"files": "gfx/layers/apz/test/mochitest/**",
|
||||
|
@ -1,5 +1,5 @@
|
||||
python
|
||||
import sys
|
||||
sys.path.append('third_party/python/gdbpp/')
|
||||
sys.path.append('python/gdbpp/')
|
||||
import gdbpp
|
||||
end
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -99,6 +99,9 @@ devtools/client/debugger/assets/module-manifest.json
|
||||
# Ignore node_modules directories in devtools
|
||||
devtools/**/node_modules
|
||||
|
||||
# Ignore browsertime output directory
|
||||
browsertime-results
|
||||
|
||||
# Tag files generated by GNU Global
|
||||
GTAGS
|
||||
GRTAGS
|
||||
|
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -1235,6 +1235,7 @@ name = "gkrust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gkrust-shared",
|
||||
"mozglue-static",
|
||||
"mozilla-central-workspace-hack",
|
||||
"stylo_tests",
|
||||
]
|
||||
@ -1245,6 +1246,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bench-collections-gtest",
|
||||
"gkrust-shared",
|
||||
"mozglue-static",
|
||||
"mp4parse-gtest",
|
||||
"nsstring-gtest",
|
||||
"xpcom-gtest",
|
||||
@ -1555,6 +1557,8 @@ name = "jsrust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"jsrust_shared",
|
||||
"mozglue-static",
|
||||
"wat",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1624,6 +1628,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||
|
||||
[[package]]
|
||||
name = "leb128"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.60"
|
||||
@ -3744,6 +3754,24 @@ version = "0.39.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a026c1436af49d5537c7561c7474f81f7a754e36445fe52e6e88795a9747291c"
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "23.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b080a48623c1b15193eac2e28c7b8d0e6b2e1c6c67ed46ddcd86063e78e504"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c350d7431aa486488d28cdf75b57d59c02fab9cde20d93c52424510afe18ecc"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webdriver"
|
||||
version = "0.40.1"
|
||||
|
@ -150,13 +150,6 @@ endif
|
||||
recurse_artifact:
|
||||
$(topsrcdir)/mach --log-no-times artifact install$(if $(MOZ_ARTIFACT_BUILD_SYMBOLS), --symbols$(addprefix =,$(filter full,$(MOZ_ARTIFACT_BUILD_SYMBOLS))))$(if $(MOZ_AUTOMATION), --host-bins)
|
||||
|
||||
ifdef MOZ_EME_WIN32_ARTIFACT
|
||||
recurse_win32-artifact:
|
||||
rm -rf $(DIST)/i686
|
||||
$(topsrcdir)/mach --log-no-times artifact install --job $(if $(MOZ_PGO),win32-pgo,win32-opt) --no-tests --distdir $(DIST)/i686
|
||||
mv $(DIST)/i686/bin/* $(DIST)/i686
|
||||
endif
|
||||
|
||||
ifdef MOZ_ANDROID_FAT_AAR_ARCHITECTURES
|
||||
recurse_android-fat-aar-artifact:
|
||||
$(call py_action,fat_aar,$(MOZ_ANDROID_FAT_AAR_ARCHITECTURES) --distdir $(abspath $(DIST)/fat-aar))
|
||||
|
@ -31,19 +31,10 @@ function isEventForAutocompleteItem(event) {
|
||||
* search isn't finished yet.
|
||||
*/
|
||||
function waitForSearchFinish() {
|
||||
if (UrlbarPrefs.get("quantumbar")) {
|
||||
return Promise.all([
|
||||
gURLBar.lastQueryContextPromise,
|
||||
BrowserTestUtils.waitForCondition(() => gURLBar.view.isOpen),
|
||||
]);
|
||||
}
|
||||
return BrowserTestUtils.waitForCondition(
|
||||
() =>
|
||||
gURLBar.popupOpen &&
|
||||
gURLBar.controller.searchStatus >=
|
||||
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH,
|
||||
"Waiting for search to complete"
|
||||
);
|
||||
return Promise.all([
|
||||
gURLBar.lastQueryContextPromise,
|
||||
BrowserTestUtils.waitForCondition(() => gURLBar.view.isOpen),
|
||||
]);
|
||||
}
|
||||
|
||||
// Check that the URL bar manages accessibility focus appropriately.
|
||||
@ -132,9 +123,7 @@ async function runTests() {
|
||||
EventUtils.synthesizeKey("KEY_ArrowLeft");
|
||||
await focused;
|
||||
testStates(textBox, STATE_FOCUSED);
|
||||
if (UrlbarPrefs.get("quantumbar")) {
|
||||
gURLBar.view.close();
|
||||
}
|
||||
gURLBar.view.close();
|
||||
// On Mac, down arrow when not at the end of the field moves to the end.
|
||||
// Move back to the end so the next press of down arrow opens the popup.
|
||||
EventUtils.synthesizeKey("KEY_ArrowRight");
|
||||
|
@ -14,9 +14,7 @@ add_task(async function testAutocompleteRichResult() {
|
||||
let tab = await openNewTab("data:text/html;charset=utf-8,");
|
||||
let accService = await initAccessibilityService();
|
||||
|
||||
info(
|
||||
"Opening the URL bar and entering a key to show the PopupAutoCompleteRichResult panel"
|
||||
);
|
||||
info("Opening the URL bar and entering a key to show the urlbar panel");
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
waitForFocus,
|
||||
@ -25,22 +23,10 @@ add_task(async function testAutocompleteRichResult() {
|
||||
|
||||
info("Waiting for accessibility to be created for the richlistbox");
|
||||
let resultsView;
|
||||
if (UrlbarPrefs.get("quantumbar")) {
|
||||
resultsView = gURLBar.view.panel.querySelector("#urlbarView-results");
|
||||
await BrowserTestUtils.waitForCondition(() =>
|
||||
accService.getAccessibleFor(resultsView)
|
||||
);
|
||||
} else {
|
||||
let urlbarPopup = document.getElementById("PopupAutoCompleteRichResult");
|
||||
resultsView = document.getAnonymousElementByAttribute(
|
||||
urlbarPopup,
|
||||
"anonid",
|
||||
"richlistbox"
|
||||
);
|
||||
await BrowserTestUtils.waitForCondition(() =>
|
||||
accService.getAccessibleFor(resultsView)
|
||||
);
|
||||
}
|
||||
resultsView = gURLBar.view.panel.querySelector("#urlbarView-results");
|
||||
await BrowserTestUtils.waitForCondition(() =>
|
||||
accService.getAccessibleFor(resultsView)
|
||||
);
|
||||
|
||||
info("Confirming that the special case is handled in XULListboxAccessible");
|
||||
let accessible = accService.getAccessibleFor(resultsView);
|
||||
|
@ -27,6 +27,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
InlineSpellCheckerContent:
|
||||
"resource://gre/modules/InlineSpellCheckerContent.jsm",
|
||||
ContentDOMReference: "resource://gre/modules/ContentDOMReference.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PageMenuChild", () => {
|
||||
@ -188,6 +189,8 @@ const messageListeners = {
|
||||
let ctxDraw = canvas.getContext("2d");
|
||||
ctxDraw.drawImage(video, 0, 0);
|
||||
|
||||
// Note: if changing the content type, don't forget to update
|
||||
// consumers that also hardcode this content type.
|
||||
this.mm.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", {
|
||||
dataURL: canvas.toDataURL("image/jpeg", ""),
|
||||
});
|
||||
@ -531,7 +534,7 @@ class ContextMenuChild extends ActorChild {
|
||||
// The same-origin check will be done in nsContextMenu.openLinkInTab.
|
||||
let parentAllowsMixedContent = !!this.docShell.mixedContentChannel;
|
||||
|
||||
let disableSetDesktopBg = null;
|
||||
let disableSetDesktopBackground = null;
|
||||
|
||||
// Media related cache info parent needs for saving
|
||||
let contentType = null;
|
||||
@ -541,7 +544,7 @@ class ContextMenuChild extends ActorChild {
|
||||
aEvent.composedTarget instanceof Ci.nsIImageLoadingContent &&
|
||||
aEvent.composedTarget.currentURI
|
||||
) {
|
||||
disableSetDesktopBg = this._disableSetDesktopBackground(
|
||||
disableSetDesktopBackground = this._disableSetDesktopBackground(
|
||||
aEvent.composedTarget
|
||||
);
|
||||
|
||||
@ -587,6 +590,7 @@ class ContextMenuChild extends ActorChild {
|
||||
Ci.nsIReferrerInfo
|
||||
);
|
||||
referrerInfo.initWithElement(aEvent.composedTarget);
|
||||
referrerInfo = E10SUtils.serializeReferrerInfo(referrerInfo);
|
||||
|
||||
// In the case "onLink" we may have to send link referrerInfo to use in
|
||||
// _openLinkInParameters
|
||||
@ -598,45 +602,38 @@ class ContextMenuChild extends ActorChild {
|
||||
linkReferrerInfo.initWithElement(context.link);
|
||||
}
|
||||
|
||||
let targetAsCPOW = context.target;
|
||||
if (targetAsCPOW) {
|
||||
let target = context.target;
|
||||
if (target) {
|
||||
this._cleanContext();
|
||||
}
|
||||
|
||||
let isRemote =
|
||||
Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
||||
editFlags = SpellCheckHelper.isEditable(
|
||||
aEvent.composedTarget,
|
||||
this.content
|
||||
);
|
||||
|
||||
if (isRemote) {
|
||||
editFlags = SpellCheckHelper.isEditable(
|
||||
aEvent.composedTarget,
|
||||
this.content
|
||||
if (editFlags & SpellCheckHelper.SPELLCHECKABLE) {
|
||||
spellInfo = InlineSpellCheckerContent.initContextMenu(
|
||||
aEvent,
|
||||
editFlags,
|
||||
this
|
||||
);
|
||||
|
||||
if (editFlags & SpellCheckHelper.SPELLCHECKABLE) {
|
||||
spellInfo = InlineSpellCheckerContent.initContextMenu(
|
||||
aEvent,
|
||||
editFlags,
|
||||
this.mm
|
||||
);
|
||||
}
|
||||
|
||||
// Set the event target first as the copy image command needs it to
|
||||
// determine what was context-clicked on. Then, update the state of the
|
||||
// commands on the context menu.
|
||||
this.docShell.contentViewer
|
||||
.QueryInterface(Ci.nsIContentViewerEdit)
|
||||
.setCommandNode(aEvent.composedTarget);
|
||||
aEvent.composedTarget.ownerGlobal.updateCommands("contentcontextmenu");
|
||||
|
||||
customMenuItems = PageMenuChild.build(aEvent.composedTarget);
|
||||
principal = doc.nodePrincipal;
|
||||
}
|
||||
|
||||
// Set the event target first as the copy image command needs it to
|
||||
// determine what was context-clicked on. Then, update the state of the
|
||||
// commands on the context menu.
|
||||
this.docShell.contentViewer
|
||||
.QueryInterface(Ci.nsIContentViewerEdit)
|
||||
.setCommandNode(aEvent.composedTarget);
|
||||
aEvent.composedTarget.ownerGlobal.updateCommands("contentcontextmenu");
|
||||
|
||||
principal = doc.nodePrincipal;
|
||||
|
||||
let data = {
|
||||
context,
|
||||
charSet,
|
||||
baseURI,
|
||||
isRemote,
|
||||
referrerInfo,
|
||||
editFlags,
|
||||
principal,
|
||||
@ -650,26 +647,22 @@ class ContextMenuChild extends ActorChild {
|
||||
contentDisposition,
|
||||
frameOuterWindowID,
|
||||
popupNodeSelectors,
|
||||
disableSetDesktopBg,
|
||||
disableSetDesktopBackground,
|
||||
parentAllowsMixedContent,
|
||||
};
|
||||
|
||||
if (context.inFrame && !context.inSrcdocFrame) {
|
||||
if (isRemote) {
|
||||
data.frameReferrerInfo = E10SUtils.serializeReferrerInfo(
|
||||
doc.referrerInfo
|
||||
);
|
||||
} else {
|
||||
data.frameReferrerInfo = doc.referrerInfo;
|
||||
}
|
||||
data.frameReferrerInfo = E10SUtils.serializeReferrerInfo(
|
||||
doc.referrerInfo
|
||||
);
|
||||
}
|
||||
|
||||
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
data.customMenuItems = PageMenuChild.build(aEvent.composedTarget);
|
||||
}
|
||||
|
||||
if (linkReferrerInfo) {
|
||||
if (isRemote) {
|
||||
data.linkReferrerInfo = E10SUtils.serializeReferrerInfo(linkReferrerInfo);
|
||||
} else {
|
||||
data.linkReferrerInfo = linkReferrerInfo;
|
||||
}
|
||||
data.linkReferrerInfo = E10SUtils.serializeReferrerInfo(linkReferrerInfo);
|
||||
}
|
||||
|
||||
Services.obs.notifyObservers(
|
||||
@ -677,27 +670,14 @@ class ContextMenuChild extends ActorChild {
|
||||
"on-prepare-contextmenu"
|
||||
);
|
||||
|
||||
if (isRemote) {
|
||||
data.referrerInfo = E10SUtils.serializeReferrerInfo(data.referrerInfo);
|
||||
if (data.frameReferrerInfo) {
|
||||
data.frameReferrerInfo =
|
||||
E10SUtils.serializeReferrerInfo(data.frameReferrerInfo);
|
||||
}
|
||||
// In the event that the content is running in the parent process, we don't
|
||||
// actually want the contextmenu events to reach the parent - we'll dispatch
|
||||
// a new contextmenu event after the async message has reached the parent
|
||||
// instead.
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
|
||||
this.mm.sendAsyncMessage("contextmenu", data, {
|
||||
targetAsCPOW,
|
||||
});
|
||||
} else {
|
||||
let browser = this.docShell.chromeEventHandler;
|
||||
let mainWin = browser.ownerGlobal;
|
||||
|
||||
data.documentURIObject = doc.documentURIObject;
|
||||
data.disableSetDesktopBackground = data.disableSetDesktopBg;
|
||||
delete data.disableSetDesktopBg;
|
||||
|
||||
data.context.targetAsCPOW = targetAsCPOW;
|
||||
mainWin.setContextMenuContentData(data);
|
||||
}
|
||||
this.mm.sendAsyncMessage("contextmenu", data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -869,6 +849,7 @@ class ContextMenuChild extends ActorChild {
|
||||
// Remember the node and its owner document that was clicked
|
||||
// This may be modifed before sending to nsContextMenu
|
||||
context.target = node;
|
||||
context.targetIdentifier = ContentDOMReference.get(node);
|
||||
|
||||
context.principal = context.target.ownerDocument.nodePrincipal;
|
||||
context.storagePrincipal =
|
||||
|
@ -6,13 +6,6 @@
|
||||
# * permission is an integer between 1 and 15
|
||||
# See nsPermissionManager.cpp for more...
|
||||
|
||||
# UITour
|
||||
origin uitour 1 https://www.mozilla.org
|
||||
origin uitour 1 https://screenshots.firefox.com
|
||||
origin uitour 1 https://support.mozilla.org
|
||||
origin uitour 1 about:home
|
||||
origin uitour 1 about:newtab
|
||||
|
||||
# Remote troubleshooting
|
||||
origin remote-troubleshooting 1 https://support.mozilla.org
|
||||
|
||||
|
@ -147,15 +147,6 @@ pref("lightweightThemes.getMoreURL", "data:text/plain,");
|
||||
pref("browser.eme.ui.enabled", false);
|
||||
#endif
|
||||
|
||||
// UI tour experience.
|
||||
pref("browser.uitour.enabled", false);
|
||||
pref("browser.uitour.loglevel", "Error");
|
||||
pref("browser.uitour.requireSecure", true);
|
||||
pref("browser.uitour.themeOrigin", "data:text/plain,");
|
||||
pref("browser.uitour.url", "data:text/plain,");
|
||||
// How long to show a Hearbeat survey (two hours, in seconds)
|
||||
pref("browser.uitour.surveyDuration", 7200);
|
||||
|
||||
pref("keyword.enabled", false);
|
||||
pref("browser.fixup.domainwhitelist.localhost", true);
|
||||
|
||||
@ -1612,9 +1603,6 @@ pref("dom.ipc.processPrelaunch.enabled", true);
|
||||
pref("browser.migrate.chrome.history.limit", 2000);
|
||||
pref("browser.migrate.chrome.history.maxAgeInDays", 180);
|
||||
|
||||
// Enable browser frames for use on desktop. Only exposed to chrome callers.
|
||||
pref("dom.mozBrowserFramesEnabled", true);
|
||||
|
||||
pref("signon.generation.available", true);
|
||||
pref("signon.generation.enabled", true);
|
||||
pref("signon.schemeUpgrades", true);
|
||||
|
@ -572,10 +572,6 @@
|
||||
#else
|
||||
/>
|
||||
#endif
|
||||
<menuitem id="menu_openTour"
|
||||
oncommand="openTourPage();"
|
||||
label="&helpShowTour2.label;"
|
||||
accesskey="&helpShowTour2.accesskey;"/>
|
||||
<menuitem id="menu_keyboardShortcuts"
|
||||
oncommand="openHelpLink('')"
|
||||
onclick="checkForMiddleClick(this, event);"
|
||||
|
@ -78,7 +78,7 @@
|
||||
#toolbar-menubar[autohide="true"][inactive="true"]:not([customizing="true"]) {
|
||||
min-height: 0 !important;
|
||||
height: 0 !important;
|
||||
-moz-appearance: none !important;
|
||||
appearance: none !important;
|
||||
}
|
||||
%endif
|
||||
|
||||
@ -321,7 +321,8 @@ window:not([chromehidden~="toolbar"]) #nav-bar[nonemptyoverflow] > .overflow-but
|
||||
}
|
||||
|
||||
.titlebar-buttonbox {
|
||||
-moz-appearance: -moz-window-button-box;
|
||||
appearance: auto;
|
||||
-moz-default-appearance: -moz-window-button-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -340,7 +341,8 @@ toolbarpaletteitem {
|
||||
|
||||
%ifdef XP_MACOSX
|
||||
#titlebar-fullscreen-button {
|
||||
-moz-appearance: -moz-mac-fullscreen-button;
|
||||
appearance: auto;
|
||||
-moz-default-appearance: -moz-mac-fullscreen-button;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -541,9 +543,6 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks {
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
#urlbar[quantumbar="false"] {
|
||||
-moz-binding: url(chrome://browser/content/urlbarBindings.xml#legacy-urlbar);
|
||||
}
|
||||
|
||||
#urlbar[quantumbar="true"] {
|
||||
-moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
|
||||
@ -624,71 +623,6 @@ html|input.urlbar-input {
|
||||
}
|
||||
}
|
||||
|
||||
/* Always show URLs LTR. */
|
||||
.ac-url-text:-moz-locale-dir(rtl),
|
||||
.ac-title-text[lookslikeurl]:-moz-locale-dir(rtl) {
|
||||
direction: ltr !important;
|
||||
}
|
||||
|
||||
/* Never show a scrollbar for the Location Bar popup. This overrides the
|
||||
richlistbox inline overflow: auto style.*/
|
||||
#PopupAutoCompleteRichResult > richlistbox {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* For non-action items, hide the action text; for action items, hide the URL
|
||||
text. Don't show the separator for keyword results. */
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-type-icon,
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-site-icon,
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-tags:not([empty]),
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-separator:not([type=keyword]),
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-url:not([actiontype]),
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-action[actiontype],
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem[selected] > .ac-url[actiontype=remotetab],
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem:hover > .ac-url[actiontype=remotetab]
|
||||
{
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem[selected] > .ac-action[actiontype=remotetab],
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem:hover > .ac-action[actiontype=remotetab] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Normally we want to show the "Search with Engine" label only on hover or
|
||||
selection, but we also want to show it for "alias offer" results -- that is,
|
||||
non-heuristic search engine results that have an alias and empty query.
|
||||
These results tell the user that they can be used to search. */
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover):not(.aliasOffer) > .ac-action[actiontype=searchengine],
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover):not(.aliasOffer) > .ac-separator[actiontype=searchengine] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide the em-dash separator between the search query description (.ac-title)
|
||||
and the "Search with Foo" description (.ac-url) if the search query is the
|
||||
empty string. Also hide .ac-title itself because it has a margin-inline-end;
|
||||
alternatively we would need to remove that margin in this case. */
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem.emptySearchQuery > .ac-separator,
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem.emptySearchQuery > .ac-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-site-icon {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
/* For action items in a noactions popup, show the URL text and hide the action
|
||||
text and type icon. */
|
||||
#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-url {
|
||||
display: -moz-box;
|
||||
}
|
||||
#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-action {
|
||||
display: none;
|
||||
}
|
||||
#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-type-icon {
|
||||
list-style-image: none;
|
||||
}
|
||||
|
||||
#urlbar[actionoverride] > #urlbar-display-box,
|
||||
#urlbar:not([actiontype="switchtab"]):not([actiontype="extension"]) > #urlbar-display-box,
|
||||
#urlbar:not([actiontype="switchtab"]) > #urlbar-display-box > #switchtab,
|
||||
@ -733,31 +667,6 @@ html|input.urlbar-input {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult {
|
||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup");
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult.showSearchSuggestionsNotification {
|
||||
transition: height 100ms;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] {
|
||||
display: none;
|
||||
transition: margin-top 100ms;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult.showSearchSuggestionsNotification > deck[anonid="search-suggestions-notification"] {
|
||||
display: -moz-deck;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult > richlistbox {
|
||||
transition: height 100ms;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult.showSearchSuggestionsNotification > richlistbox {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate=invalid] > #page-action-buttons > .urlbar-page-action,
|
||||
#identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action:not(#star-button-box),
|
||||
.urlbar-history-dropmarker[usertyping],
|
||||
@ -1076,7 +985,7 @@ toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-
|
||||
|
||||
/* Give this menupopup an arrow panel styling */
|
||||
#BMB_bookmarksPopup {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
-moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-arrow");
|
||||
background: transparent;
|
||||
border: none;
|
||||
@ -1217,8 +1126,7 @@ toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-
|
||||
}
|
||||
|
||||
toolbarpaletteitem[dragover] {
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-inline-color: transparent;
|
||||
}
|
||||
|
||||
#customization-palette-container {
|
||||
@ -1278,71 +1186,6 @@ toolbarpaletteitem > toolbaritem {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* UI Tour */
|
||||
|
||||
@keyframes uitour-wobble {
|
||||
from {
|
||||
transform: rotate(0deg) translateX(3px) rotate(0deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(360deg) translateX(3px) rotate(-360deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(720deg) translateX(0px) rotate(-720deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-zoom {
|
||||
from {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
to {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-color {
|
||||
from {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
50% {
|
||||
border-color: #FF0000;
|
||||
}
|
||||
to {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
}
|
||||
|
||||
#UITourHighlightContainer,
|
||||
#UITourHighlight {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#UITourHighlight[active] {
|
||||
animation-delay: 2s;
|
||||
animation-fill-mode: forwards;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
#UITourHighlight[active="wobble"] {
|
||||
animation-name: uitour-wobble;
|
||||
animation-delay: 0s;
|
||||
animation-duration: 1.5s;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
#UITourHighlight[active="zoom"] {
|
||||
animation-name: uitour-zoom;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
#UITourHighlight[active="color"] {
|
||||
animation-name: uitour-color;
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
/* Combined context-menu items */
|
||||
#context-navigation > .menuitem-iconic > .menu-iconic-text,
|
||||
#context-navigation > .menuitem-iconic > .menu-accel-container {
|
||||
@ -1362,7 +1205,7 @@ toolbarpaletteitem > toolbaritem {
|
||||
}
|
||||
|
||||
.dragfeedback-tab {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
opacity: 0.65;
|
||||
-moz-window-shadow: none;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
LightweightThemeConsumer:
|
||||
"resource://gre/modules/LightweightThemeConsumer.jsm",
|
||||
Log: "resource://gre/modules/Log.jsm",
|
||||
LoginHelper: "resource://gre/modules/LoginHelper.jsm",
|
||||
LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
|
||||
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
|
||||
NetUtil: "resource://gre/modules/NetUtil.jsm",
|
||||
@ -68,7 +69,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
|
||||
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
|
||||
Translation: "resource:///modules/translation/Translation.jsm",
|
||||
UITour: "resource:///modules/UITour.jsm",
|
||||
UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
|
||||
UrlbarInput: "resource:///modules/UrlbarInput.jsm",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
|
||||
@ -182,7 +182,7 @@ XPCOMUtils.defineLazyScriptGetter(
|
||||
);
|
||||
XPCOMUtils.defineLazyScriptGetter(
|
||||
this,
|
||||
["setContextMenuContentData", "openContextMenu", "nsContextMenu"],
|
||||
["openContextMenu", "nsContextMenu"],
|
||||
"chrome://browser/content/nsContextMenu.js"
|
||||
);
|
||||
XPCOMUtils.defineLazyScriptGetter(
|
||||
@ -290,60 +290,30 @@ XPCOMUtils.defineLazyGetter(this, "gURLBar", () => gURLBarHandler.urlbar);
|
||||
|
||||
/**
|
||||
* Tracks the urlbar object, allowing to reinitiate it when necessary, e.g. on
|
||||
* customization or when the quantumbar pref changes.
|
||||
* customization.
|
||||
*/
|
||||
var gURLBarHandler = {
|
||||
toggleQuantumBarAttribute() {
|
||||
this.textbox = document.getElementById("urlbar");
|
||||
this.textbox.setAttribute("quantumbar", this.quantumbar);
|
||||
},
|
||||
|
||||
/**
|
||||
* The urlbar binding or object.
|
||||
*/
|
||||
get urlbar() {
|
||||
if (!this._urlbar) {
|
||||
if (this.quantumbar) {
|
||||
this._urlbar = new UrlbarInput({ textbox: this.textbox });
|
||||
if (this._lastValue) {
|
||||
this._urlbar.value = this._lastValue;
|
||||
delete this._lastValue;
|
||||
}
|
||||
} else {
|
||||
this._urlbar = this.textbox;
|
||||
let textbox = document.getElementById("urlbar");
|
||||
this._urlbar = new UrlbarInput({ textbox });
|
||||
if (this._lastValue) {
|
||||
this._urlbar.value = this._lastValue;
|
||||
delete this._lastValue;
|
||||
}
|
||||
gBrowser.tabContainer.addEventListener("TabSelect", this._urlbar);
|
||||
}
|
||||
return this._urlbar;
|
||||
},
|
||||
|
||||
/**
|
||||
* Forwards to gURLBar.formatValue(), if the binding has been applied already.
|
||||
* This is necessary until the Quantum Bar is not the default and we allow
|
||||
* to dynamically switch between it and the legacy implementation, because the
|
||||
* binding is only applied before the initial xul layout.
|
||||
*/
|
||||
formatValue() {
|
||||
if (this.quantumbar) {
|
||||
this.urlbar.formatValue();
|
||||
} else if (typeof this.textbox.formatValue == "function") {
|
||||
this.textbox.formatValue();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked when the quantumbar pref changes.
|
||||
*/
|
||||
handlePrefChange() {
|
||||
this._updateBinding();
|
||||
this._reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked by CustomizationHandler when a customization starts.
|
||||
*/
|
||||
customizeStart() {
|
||||
if (this._urlbar && this._urlbar.constructor.name == "UrlbarInput") {
|
||||
if (this._urlbar) {
|
||||
this._urlbar.removeCopyCutController();
|
||||
}
|
||||
},
|
||||
@ -355,44 +325,20 @@ var gURLBarHandler = {
|
||||
this._reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Rebuilds the textbox binding by detaching the element when necessary.
|
||||
*/
|
||||
_updateBinding() {
|
||||
let quantumbarApplied = this.textbox.getAttribute("quantumbar") == "true";
|
||||
if (quantumbarApplied != this.quantumbar) {
|
||||
let placeholder = document.createXULElement("toolbarpaletteitem");
|
||||
let parent = this.textbox.parentNode;
|
||||
parent.replaceChild(placeholder, this.textbox);
|
||||
this.textbox.setAttribute("quantumbar", this.quantumbar);
|
||||
parent.replaceChild(this.textbox, placeholder);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to reset the gURLBar value.
|
||||
*/
|
||||
_reset() {
|
||||
if (this._urlbar) {
|
||||
gBrowser.tabContainer.removeEventListener("TabSelect", this._urlbar);
|
||||
if (this._urlbar.constructor.name == "UrlbarInput") {
|
||||
this._lastValue = this._urlbar.value;
|
||||
this._urlbar.uninit();
|
||||
}
|
||||
this._lastValue = this._urlbar.value;
|
||||
this._urlbar.uninit();
|
||||
delete this._urlbar;
|
||||
gURLBar = this.urlbar;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
gURLBarHandler,
|
||||
"quantumbar",
|
||||
"browser.urlbar.quantumbar",
|
||||
false,
|
||||
gURLBarHandler.handlePrefChange.bind(gURLBarHandler)
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
|
||||
Components.Constructor(
|
||||
"@mozilla.org/referrer-info;1",
|
||||
@ -1666,8 +1612,9 @@ var gBrowserInit = {
|
||||
},
|
||||
|
||||
onBeforeInitialXULLayout() {
|
||||
// Dynamically switch on-off the Quantum Bar based on prefs.
|
||||
gURLBarHandler.toggleQuantumBarAttribute();
|
||||
// Turn on QuantumBar. This can be removed once the quantumbar attribute is gone.
|
||||
let urlbar = document.getElementById("urlbar");
|
||||
urlbar.setAttribute("quantumbar", true);
|
||||
|
||||
// Set a sane starting width/height for all resolutions on new profiles.
|
||||
if (Services.prefs.getBoolPref("privacy.resistFingerprinting")) {
|
||||
@ -2807,29 +2754,17 @@ function loadOneOrMoreURIs(aURIString, aTriggeringPrincipal, aCsp) {
|
||||
|
||||
/**
|
||||
* Focuses the location bar input field and selects its contents.
|
||||
*
|
||||
* @param [optional] userInitiatedFocus
|
||||
* Whether this focus is caused by an user interaction whose intention
|
||||
* was to use the location bar. For example, using a shortcut to go to
|
||||
* the location bar, or a contextual menu to search from it.
|
||||
* The default is false and should be used in all those cases where the
|
||||
* code focuses the location bar but that's not the primary user
|
||||
* intention, like when opening a new tab.
|
||||
*/
|
||||
function focusAndSelectUrlBar(userInitiatedFocus = false) {
|
||||
function focusAndSelectUrlBar() {
|
||||
// In customize mode, the url bar is disabled. If a new tab is opened or the
|
||||
// user switches to a different tab, this function gets called before we've
|
||||
// finished leaving customize mode, and the url bar will still be disabled.
|
||||
// We can't focus it when it's disabled, so we need to re-run ourselves when
|
||||
// we've finished leaving customize mode.
|
||||
if (CustomizationHandler.isExitingCustomizeMode) {
|
||||
gNavToolbox.addEventListener(
|
||||
"aftercustomization",
|
||||
function() {
|
||||
focusAndSelectUrlBar(userInitiatedFocus);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
if (CustomizationHandler.isCustomizing()) {
|
||||
gNavToolbox.addEventListener("aftercustomization", focusAndSelectUrlBar, {
|
||||
once: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2837,14 +2772,15 @@ function focusAndSelectUrlBar(userInitiatedFocus = false) {
|
||||
FullScreen.showNavToolbox();
|
||||
}
|
||||
|
||||
gURLBar.userInitiatedFocus = userInitiatedFocus;
|
||||
gURLBar.select();
|
||||
gURLBar.userInitiatedFocus = false;
|
||||
}
|
||||
|
||||
function openLocation() {
|
||||
if (window.location.href == AppConstants.BROWSER_CHROME_URL) {
|
||||
focusAndSelectUrlBar(true);
|
||||
focusAndSelectUrlBar();
|
||||
if (gURLBar.openViewOnFocus && !gURLBar.view.isOpen) {
|
||||
gURLBar.startQuery();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3090,13 +3026,8 @@ function readFromClipboard() {
|
||||
/**
|
||||
* Open the View Source dialog.
|
||||
*
|
||||
* @param aArgsOrDocument
|
||||
* Either an object or a Document. Passing a Document is deprecated,
|
||||
* and is not supported with e10s. This function will throw if
|
||||
* aArgsOrDocument is a CPOW.
|
||||
*
|
||||
* If aArgsOrDocument is an object, that object can take the
|
||||
* following properties:
|
||||
* @param args
|
||||
* An object with the following properties:
|
||||
*
|
||||
* URL (required):
|
||||
* A string URL for the page we'd like to view the source of.
|
||||
@ -3111,27 +3042,7 @@ function readFromClipboard() {
|
||||
* lineNumber (optional):
|
||||
* The line number to focus on once the source is loaded.
|
||||
*/
|
||||
async function BrowserViewSourceOfDocument(aArgsOrDocument) {
|
||||
let args;
|
||||
|
||||
if (aArgsOrDocument instanceof Document) {
|
||||
let doc = aArgsOrDocument;
|
||||
// Deprecated API - callers should pass args object instead.
|
||||
if (Cu.isCrossProcessWrapper(doc)) {
|
||||
throw new Error(
|
||||
"BrowserViewSourceOfDocument cannot accept a CPOW as a document."
|
||||
);
|
||||
}
|
||||
|
||||
let win = doc.defaultView;
|
||||
let browser = win.docShell.chromeEventHandler;
|
||||
let outerWindowID = win.windowUtils.outerWindowID;
|
||||
let URL = browser.currentURI.spec;
|
||||
args = { browser, outerWindowID, URL };
|
||||
} else {
|
||||
args = aArgsOrDocument;
|
||||
}
|
||||
|
||||
async function BrowserViewSourceOfDocument(args) {
|
||||
// Check if external view source is enabled. If so, try it. If it fails,
|
||||
// fallback to internal view source.
|
||||
if (Services.prefs.getBoolPref("view_source.editor.external")) {
|
||||
@ -4089,9 +4000,7 @@ function getDetailedCertErrorInfo(location, securityInfo) {
|
||||
hasHPKP,
|
||||
]);
|
||||
|
||||
certErrorDetails +=
|
||||
"\r\n\r\n" +
|
||||
gNavigatorBundle.getString("certErrorDetailsCertChain.label");
|
||||
certErrorDetails;
|
||||
|
||||
return certErrorDetails;
|
||||
}
|
||||
@ -5975,7 +5884,7 @@ var XULBrowserWindow = {
|
||||
|
||||
// Make sure the "https" part of the URL is striked out or not,
|
||||
// depending on the current mixed active content blocking state.
|
||||
gURLBarHandler.formatValue();
|
||||
gURLBar.formatValue();
|
||||
|
||||
try {
|
||||
uri = Services.io.createExposableURI(uri);
|
||||
@ -8917,58 +8826,39 @@ var gPrivateBrowsingUI = {
|
||||
// temporary fix until bug 463607 is fixed
|
||||
document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
|
||||
|
||||
if (window.location.href == AppConstants.BROWSER_CHROME_URL) {
|
||||
// Adjust the window's title
|
||||
let docElement = document.documentElement;
|
||||
if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||
docElement.setAttribute(
|
||||
"title",
|
||||
docElement.getAttribute("title_privatebrowsing")
|
||||
);
|
||||
docElement.setAttribute(
|
||||
"titlemodifier",
|
||||
docElement.getAttribute("titlemodifier_privatebrowsing")
|
||||
);
|
||||
}
|
||||
if (window.location.href != AppConstants.BROWSER_CHROME_URL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the window's title
|
||||
let docElement = document.documentElement;
|
||||
if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||
docElement.setAttribute(
|
||||
"privatebrowsingmode",
|
||||
PrivateBrowsingUtils.permanentPrivateBrowsing
|
||||
? "permanent"
|
||||
: "temporary"
|
||||
"title",
|
||||
docElement.getAttribute("title_privatebrowsing")
|
||||
);
|
||||
gBrowser.updateTitlebar();
|
||||
docElement.setAttribute(
|
||||
"titlemodifier",
|
||||
docElement.getAttribute("titlemodifier_privatebrowsing")
|
||||
);
|
||||
}
|
||||
docElement.setAttribute(
|
||||
"privatebrowsingmode",
|
||||
PrivateBrowsingUtils.permanentPrivateBrowsing ? "permanent" : "temporary"
|
||||
);
|
||||
gBrowser.updateTitlebar();
|
||||
|
||||
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||
// Adjust the New Window menu entries
|
||||
[
|
||||
{ normal: "menu_newNavigator", private: "menu_newPrivateWindow" },
|
||||
].forEach(function(menu) {
|
||||
let newWindow = document.getElementById(menu.normal);
|
||||
let newPrivateWindow = document.getElementById(menu.private);
|
||||
if (newWindow && newPrivateWindow) {
|
||||
newPrivateWindow.hidden = true;
|
||||
newWindow.label = newPrivateWindow.label;
|
||||
newWindow.accessKey = newPrivateWindow.accessKey;
|
||||
newWindow.command = newPrivateWindow.command;
|
||||
}
|
||||
});
|
||||
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||
// Adjust the New Window menu entries
|
||||
let newWindow = document.getElementById("menu_newNavigator");
|
||||
let newPrivateWindow = document.getElementById("menu_newPrivateWindow");
|
||||
if (newWindow && newPrivateWindow) {
|
||||
newPrivateWindow.hidden = true;
|
||||
newWindow.label = newPrivateWindow.label;
|
||||
newWindow.accessKey = newPrivateWindow.accessKey;
|
||||
newWindow.command = newPrivateWindow.command;
|
||||
}
|
||||
}
|
||||
|
||||
let urlBarSearchParam =
|
||||
gURLBar.getAttribute("autocompletesearchparam") || "";
|
||||
if (
|
||||
!PrivateBrowsingUtils.permanentPrivateBrowsing &&
|
||||
!urlBarSearchParam.includes("disable-private-actions")
|
||||
) {
|
||||
// Disable switch to tab autocompletion for private windows.
|
||||
// We leave it enabled for permanent private browsing mode though.
|
||||
urlBarSearchParam += " disable-private-actions";
|
||||
}
|
||||
if (!urlBarSearchParam.includes("private-window")) {
|
||||
urlBarSearchParam += " private-window";
|
||||
}
|
||||
gURLBar.setAttribute("autocompletesearchparam", urlBarSearchParam);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -221,16 +221,6 @@
|
||||
noautofocus="true"
|
||||
hidden="true" />
|
||||
|
||||
<!-- for url bar autocomplete -->
|
||||
<panel type="autocomplete-richlistbox"
|
||||
id="PopupAutoCompleteRichResult"
|
||||
role="group"
|
||||
noautofocus="true"
|
||||
hidden="true"
|
||||
flip="none"
|
||||
level="parent"
|
||||
overflowpadding="15" />
|
||||
|
||||
<!-- for date/time picker. consumeoutsideclicks is set to never, so that
|
||||
clicks on the anchored input box are never consumed. -->
|
||||
<panel id="DateTimePickerPanel"
|
||||
@ -313,42 +303,6 @@
|
||||
</hbox>
|
||||
</panel>
|
||||
|
||||
<!-- UI tour experience -->
|
||||
<panel id="UITourTooltip"
|
||||
type="arrow"
|
||||
hidden="true"
|
||||
noautofocus="true"
|
||||
noautohide="true"
|
||||
align="start"
|
||||
orient="vertical"
|
||||
role="alert">
|
||||
<vbox>
|
||||
<hbox id="UITourTooltipBody">
|
||||
<image id="UITourTooltipIcon"/>
|
||||
<vbox flex="1">
|
||||
<hbox id="UITourTooltipTitleContainer">
|
||||
<label id="UITourTooltipTitle" flex="1"/>
|
||||
<toolbarbutton id="UITourTooltipClose" class="close-icon"
|
||||
tooltiptext="&uiTour.infoPanel.close;"/>
|
||||
</hbox>
|
||||
<description id="UITourTooltipDescription" flex="1"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<hbox id="UITourTooltipButtons" flex="1" align="center"/>
|
||||
</vbox>
|
||||
</panel>
|
||||
<!-- type="default" forces frames to be created so that the panel's size can be determined -->
|
||||
<panel id="UITourHighlightContainer"
|
||||
type="default"
|
||||
hidden="true"
|
||||
noautofocus="true"
|
||||
noautohide="true"
|
||||
flip="none"
|
||||
consumeoutsideclicks="false"
|
||||
mousethrough="always">
|
||||
<box id="UITourHighlight"></box>
|
||||
</panel>
|
||||
|
||||
<panel id="sidebarMenu-popup"
|
||||
class="cui-widget-panel"
|
||||
role="group"
|
||||
|
@ -19,7 +19,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
|
||||
LoginFormFactory: "resource://gre/modules/LoginFormFactory.jsm",
|
||||
InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
|
||||
ContextMenuChild: "resource:///actors/ContextMenuChild.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
|
||||
@ -32,11 +31,6 @@ XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
|
||||
// NOTE: Much of this logic is duplicated in BrowserCLH.js for Android.
|
||||
addMessageListener("PasswordManager:fillForm", function(message) {
|
||||
// intercept if ContextMenu.jsm had sent a plain object for remote targets
|
||||
message.objects.inputElement = ContextMenuChild.getTarget(
|
||||
global,
|
||||
message,
|
||||
"inputElement"
|
||||
);
|
||||
LoginManagerContent.receiveMessage(message, content);
|
||||
});
|
||||
addMessageListener("PasswordManager:fillGeneratedPassword", function(message) {
|
||||
|
@ -8,17 +8,12 @@
|
||||
border-top: none;
|
||||
box-shadow: 0 5px 10px hsla(0, 0%, 0%, .1);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
inset-inline-start: 0;
|
||||
z-index: 1001;
|
||||
-moz-user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.contentSearchSuggestionTable:dir(rtl) {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.contentSearchSuggestionsList {
|
||||
border-bottom: 1px solid hsl(0, 0%, 92%);
|
||||
width: 100%;
|
||||
@ -106,7 +101,7 @@
|
||||
}
|
||||
|
||||
.contentSearchOneOffItem {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
height: 32px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -118,7 +113,7 @@
|
||||
}
|
||||
|
||||
.contentSearchOneOffItem:dir(rtl) {
|
||||
background-position: left center;
|
||||
background-position-x: left;
|
||||
}
|
||||
|
||||
.contentSearchOneOffItem > img {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,3 +17,55 @@
|
||||
/* This following entry can be removed when Bug 522850 is fixed. */
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.tableSeparator {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
font: inherit;
|
||||
text-align: start;
|
||||
padding-inline-end: .5em;
|
||||
}
|
||||
|
||||
/*
|
||||
Make the first column shrink to its min-content, except for #securityTable
|
||||
which has full sentences in its first column.
|
||||
*/
|
||||
table:not(#securityTable) th {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
th > label,
|
||||
td > input,
|
||||
.table-split-column {
|
||||
width: 100%;
|
||||
margin-block: 1px 4px;
|
||||
}
|
||||
|
||||
.table-split-column {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-split-column > label,
|
||||
.table-split-column > input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.table-split-column > button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#hostText {
|
||||
-moz-box-flex: 1;
|
||||
margin-top: 1px; /* same margin as adjacent label */
|
||||
}
|
||||
|
@ -149,7 +149,10 @@ pageInfoTreeView.prototype = {
|
||||
return 0;
|
||||
},
|
||||
getImageSrc(row, column) {},
|
||||
getCellValue(row, column) {},
|
||||
getCellValue(row, column) {
|
||||
let col = column != null ? column : this.copycol;
|
||||
return row < 0 || col < 0 ? "" : this.data[row][col] || "";
|
||||
},
|
||||
toggleOpenState(index) {},
|
||||
cycleHeader(col) {},
|
||||
selectionChanged() {},
|
||||
@ -269,6 +272,19 @@ const nsIPermissionManager = Ci.nsIPermissionManager;
|
||||
const nsICertificateDialogs = Ci.nsICertificateDialogs;
|
||||
const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1";
|
||||
|
||||
// clipboard helper
|
||||
function getClipboardHelper() {
|
||||
try {
|
||||
return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
|
||||
Ci.nsIClipboardHelper
|
||||
);
|
||||
} catch (e) {
|
||||
// do nothing, later code will handle the error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const gClipboardHelper = getClipboardHelper();
|
||||
|
||||
// namespaces, don't need all of these yet...
|
||||
const XLinkNS = "http://www.w3.org/1999/xlink";
|
||||
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
@ -1150,11 +1166,9 @@ function getContentTypeFromHeaders(cacheEntryDescriptor) {
|
||||
|
||||
function setItemValue(id, value) {
|
||||
var item = document.getElementById(id);
|
||||
item.closest("tr").hidden = !value;
|
||||
if (value) {
|
||||
item.parentNode.collapsed = false;
|
||||
item.value = value;
|
||||
} else {
|
||||
item.parentNode.collapsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1175,6 +1189,37 @@ function formatDate(datestr, unknown) {
|
||||
return dateTimeFormatter.format(date);
|
||||
}
|
||||
|
||||
function doCopy() {
|
||||
if (!gClipboardHelper) {
|
||||
return;
|
||||
}
|
||||
|
||||
var elem = document.commandDispatcher.focusedElement;
|
||||
|
||||
if (elem && elem.localName == "tree") {
|
||||
var view = elem.view;
|
||||
var selection = view.selection;
|
||||
var text = [],
|
||||
tmp = "";
|
||||
var min = {},
|
||||
max = {};
|
||||
|
||||
var count = selection.getRangeCount();
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
selection.getRangeAt(i, min, max);
|
||||
|
||||
for (var row = min.value; row <= max.value; row++) {
|
||||
tmp = view.getCellValue(row, null);
|
||||
if (tmp) {
|
||||
text.push(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
gClipboardHelper.copyString(text.join("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
function doSelectAllMedia() {
|
||||
var tree = document.getElementById("imagetree");
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
<window id="main-window"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
data-l10n-id="page-info-window"
|
||||
data-l10n-attrs="style"
|
||||
windowtype="Browser:page-info"
|
||||
@ -24,20 +25,21 @@
|
||||
screenX="10" screenY="10"
|
||||
persist="screenX screenY width height sizemode">
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include ../macWindow.inc.xul
|
||||
#endif
|
||||
|
||||
<linkset>
|
||||
<html:link rel="localization" href="browser/pageInfo.ftl"/>
|
||||
</linkset>
|
||||
#ifdef XP_MACOSX
|
||||
#include ../macWindow.inc.xul
|
||||
#else
|
||||
<script src="chrome://global/content/globalOverlay.js"/>
|
||||
<script src="chrome://global/content/editMenuOverlay.js"/>
|
||||
<script src="chrome://browser/content/utilityOverlay.js"/>
|
||||
#endif
|
||||
<script src="chrome://global/content/contentAreaUtils.js"/>
|
||||
<script src="chrome://global/content/treeUtils.js"/>
|
||||
<script src="chrome://browser/content/pageinfo/pageInfo.js"/>
|
||||
<script src="chrome://browser/content/pageinfo/permissions.js"/>
|
||||
<script src="chrome://browser/content/pageinfo/security.js"/>
|
||||
<script src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
||||
<stringbundleset id="pageinfobundleset">
|
||||
<stringbundle id="pkiBundle" src="chrome://pippki/locale/pippki.properties"/>
|
||||
@ -47,7 +49,8 @@
|
||||
<commandset id="pageInfoCommandSet">
|
||||
<command id="cmd_close" oncommand="window.close();"/>
|
||||
<command id="cmd_help" oncommand="doHelpButton();"/>
|
||||
<command id="cmd_selectall" oncommand="doSelectAll();"/>
|
||||
<command id="cmd_copy_tree" oncommand="doCopy();"/>
|
||||
<command id="cmd_selectall_tree" oncommand="doSelectAll();"/>
|
||||
</commandset>
|
||||
|
||||
<keyset id="pageInfoKeySet">
|
||||
@ -58,12 +61,14 @@
|
||||
#else
|
||||
<key keycode="VK_F1" command="cmd_help"/>
|
||||
#endif
|
||||
<key data-l10n-id="select-all" data-l10n-attrs="key" modifiers="accel" command="cmd_selectall"/>
|
||||
<key data-l10n-id="select-all" data-l10n-attrs="key" modifiers="alt" command="cmd_selectall"/>
|
||||
<key data-l10n-id="copy" data-l10n-attrs="key" modifiers="accel" command="cmd_copy_tree"/>
|
||||
<key data-l10n-id="select-all" data-l10n-attrs="key" modifiers="accel" command="cmd_selectall_tree"/>
|
||||
<key data-l10n-id="select-all" data-l10n-attrs="key" modifiers="alt" command="cmd_selectall_tree"/>
|
||||
</keyset>
|
||||
|
||||
<menupopup id="picontext">
|
||||
<menuitem id="menu_selectall" data-l10n-id="menu-select-all" command="cmd_selectall"/>
|
||||
<menuitem id="menu_selectall" data-l10n-id="menu-select-all" command="cmd_selectall_tree"/>
|
||||
<menuitem id="menu_copy" data-l10n-id="menu-copy" command="cmd_copy_tree"/>
|
||||
</menupopup>
|
||||
|
||||
<vbox id="topBar">
|
||||
@ -82,61 +87,74 @@
|
||||
<deck id="mainDeck" flex="1">
|
||||
<!-- General page information -->
|
||||
<vbox id="generalPanel">
|
||||
<grid id="generalGrid">
|
||||
<columns>
|
||||
<column/>
|
||||
<column class="gridSeparator"/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows id="generalRows">
|
||||
<row id="generalTitle">
|
||||
<label control="titletext" data-l10n-id="general-title"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="titletext"/>
|
||||
</row>
|
||||
<row id="generalURLRow">
|
||||
<label control="urltext" data-l10n-id="general-url"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="urltext"/>
|
||||
</row>
|
||||
<row id="generalSeparatorRow1">
|
||||
<separator class="thin"/>
|
||||
</row>
|
||||
<row id="generalTypeRow">
|
||||
<label control="typetext" data-l10n-id="general-type"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="typetext"/>
|
||||
</row>
|
||||
<row id="generalModeRow">
|
||||
<label control="modetext" data-l10n-id="general-mode"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" crop="end" id="modetext"/>
|
||||
</row>
|
||||
<row id="generalEncodingRow">
|
||||
<label control="encodingtext" data-l10n-id="general-encoding"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="encodingtext"/>
|
||||
</row>
|
||||
<row id="generalSizeRow">
|
||||
<label control="sizetext" data-l10n-id="general-size"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="sizetext"/>
|
||||
</row>
|
||||
<row id="generalReferrerRow">
|
||||
<label control="refertext" data-l10n-id="general-referrer"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="refertext"/>
|
||||
</row>
|
||||
<row id="generalSeparatorRow2">
|
||||
<separator class="thin"/>
|
||||
</row>
|
||||
<row id="generalModifiedRow">
|
||||
<label control="modifiedtext" data-l10n-id="general-modified"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="modifiedtext"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<table id="generalTable" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<tr id="generalTitle">
|
||||
<th>
|
||||
<xul:label control="titletext" data-l10n-id="general-title"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="titletext" data-l10n-attrs="value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="generalURLRow">
|
||||
<th>
|
||||
<xul:label control="urltext" data-l10n-id="general-url"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="urltext"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tableSeparator"/>
|
||||
<tr id="generalTypeRow">
|
||||
<th>
|
||||
<xul:label control="typetext" data-l10n-id="general-type"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="typetext"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="generalModeRow">
|
||||
<th>
|
||||
<xul:label control="modetext" data-l10n-id="general-mode"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="modetext" data-l10n-attrs="value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="generalEncodingRow">
|
||||
<th>
|
||||
<xul:label control="encodingtext" data-l10n-id="general-encoding"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="encodingtext"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="generalSizeRow">
|
||||
<th>
|
||||
<xul:label control="sizetext" data-l10n-id="general-size"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="sizetext" data-l10n-attrs="value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="generalReferrerRow">
|
||||
<th>
|
||||
<xul:label control="refertext" data-l10n-id="general-referrer"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="refertext"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tableSeparator"/>
|
||||
<tr id="generalModifiedRow">
|
||||
<th>
|
||||
<xul:label control="modifiedtext" data-l10n-id="general-modified"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="modifiedtext"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<separator class="thin"/>
|
||||
<vbox id="metaTags" flex="1">
|
||||
<label control="metatree" id="metaTagsCaption" class="header"/>
|
||||
@ -187,45 +205,56 @@
|
||||
</tree>
|
||||
<splitter orient="vertical" id="mediaSplitter"/>
|
||||
<vbox flex="1" id="mediaPreviewBox" collapsed="true">
|
||||
<grid id="mediaGrid">
|
||||
<columns>
|
||||
<column id="mediaLabelColumn"/>
|
||||
<column class="gridSeparator"/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows id="mediaRows">
|
||||
<row id="mediaLocationRow">
|
||||
<label control="imageurltext" data-l10n-id="media-location"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="imageurltext"/>
|
||||
</row>
|
||||
<row id="mediaTypeRow">
|
||||
<label control="imagetypetext" data-l10n-id="general-type"/>
|
||||
<separator/>
|
||||
<textbox id="imagetypetext"/>
|
||||
</row>
|
||||
<row id="mediaSizeRow">
|
||||
<label control="imagesizetext" data-l10n-id="general-size"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="imagesizetext"/>
|
||||
</row>
|
||||
<row id="mediaDimensionRow">
|
||||
<label control="imagedimensiontext" data-l10n-id="media-dimension"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="imagedimensiontext"/>
|
||||
</row>
|
||||
<row id="mediaTextRow">
|
||||
<label control="imagetext" data-l10n-id="media-text"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="imagetext"/>
|
||||
</row>
|
||||
<row id="mediaLongdescRow">
|
||||
<label control="imagelongdesctext" data-l10n-id="media-long-desc"/>
|
||||
<separator/>
|
||||
<textbox readonly="true" id="imagelongdesctext"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<table id="mediaTable" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<tr id="mediaLocationRow">
|
||||
<th>
|
||||
<xul:label control="imageurltext" data-l10n-id="media-location"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="imageurltext"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="mediaTypeRow">
|
||||
<th>
|
||||
<xul:label control="imagetypetext" data-l10n-id="general-type"/>
|
||||
</th>
|
||||
<td>
|
||||
<input id="imagetypetext" data-l10n-attrs="value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="mediaSizeRow">
|
||||
<th>
|
||||
<xul:label control="imagesizetext" data-l10n-id="general-size"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="imagesizetext" data-l10n-attrs="value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="mediaDimensionRow">
|
||||
<th>
|
||||
<xul:label control="imagedimensiontext" data-l10n-id="media-dimension"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="imagedimensiontext" data-l10n-attrs="value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="mediaTextRow">
|
||||
<th>
|
||||
<xul:label control="imagetext" data-l10n-id="media-text"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="imagetext"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="mediaLongdescRow">
|
||||
<th>
|
||||
<xul:label control="imagelongdesctext" data-l10n-id="media-long-desc"/>
|
||||
</th>
|
||||
<td>
|
||||
<input readonly="readonly" id="imagelongdesctext"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hbox id="imageSaveBox" align="end">
|
||||
<vbox id="blockImageBox">
|
||||
<checkbox id="blockImage" hidden="true" oncommand="onBlockImage()"
|
||||
@ -264,8 +293,7 @@
|
||||
<vbox id="permPanel">
|
||||
<hbox id="permHostBox">
|
||||
<label data-l10n-id="permissions-for" control="hostText" />
|
||||
<textbox id="hostText" class="header" readonly="true"
|
||||
crop="end" flex="1"/>
|
||||
<html:input id="hostText" class="header" readonly="readonly"/>
|
||||
</hbox>
|
||||
|
||||
<vbox id="permList" flex="1"/>
|
||||
@ -279,88 +307,102 @@
|
||||
<!-- Identity Section -->
|
||||
<groupbox>
|
||||
<label class="header" data-l10n-id="security-view-identity"/>
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<!-- Domain -->
|
||||
<row>
|
||||
<label data-l10n-id="security-view-identity-domain"
|
||||
control="security-identity-domain-value"/>
|
||||
<textbox id="security-identity-domain-value" readonly="true"/>
|
||||
</row>
|
||||
<!-- Owner -->
|
||||
<row>
|
||||
<label id="security-identity-owner-label"
|
||||
class="fieldLabel"
|
||||
data-l10n-id="security-view-identity-owner"
|
||||
control="security-identity-owner-value"/>
|
||||
<textbox id="security-identity-owner-value" readonly="true"/>
|
||||
</row>
|
||||
<!-- Verifier -->
|
||||
<row>
|
||||
<label data-l10n-id="security-view-identity-verifier"
|
||||
control="security-identity-verifier-value"/>
|
||||
<hbox align="center">
|
||||
<textbox id="security-identity-verifier-value" readonly="true"
|
||||
flex="1"/>
|
||||
<button id="security-view-cert" data-l10n-id="security-view"
|
||||
oncommand="security.viewCert();"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<!-- Certificate Validity -->
|
||||
<row id="security-identity-validity-row">
|
||||
<label data-l10n-id="security-view-identity-validity"
|
||||
control="security-identity-validity-value"/>
|
||||
<textbox id="security-identity-validity-value" readonly="true"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<table xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!-- Domain -->
|
||||
<tr>
|
||||
<th>
|
||||
<xul:label data-l10n-id="security-view-identity-domain"
|
||||
control="security-identity-domain-value"/>
|
||||
</th>
|
||||
<td>
|
||||
<input id="security-identity-domain-value" readonly="readonly"/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Owner -->
|
||||
<tr>
|
||||
<th>
|
||||
<xul:label id="security-identity-owner-label"
|
||||
class="fieldLabel"
|
||||
data-l10n-id="security-view-identity-owner"
|
||||
control="security-identity-owner-value"/>
|
||||
</th>
|
||||
<td>
|
||||
<input id="security-identity-owner-value" readonly="readonly" data-l10n-attrs="value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Verifier -->
|
||||
<tr>
|
||||
<th>
|
||||
<xul:label data-l10n-id="security-view-identity-verifier"
|
||||
control="security-identity-verifier-value"/>
|
||||
</th>
|
||||
<td>
|
||||
<div class="table-split-column">
|
||||
<input id="security-identity-verifier-value" readonly="readonly"
|
||||
data-l10n-attrs="value"/>
|
||||
<xul:button id="security-view-cert" data-l10n-id="security-view"
|
||||
oncommand="security.viewCert();"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Certificate Validity -->
|
||||
<tr id="security-identity-validity-row">
|
||||
<th>
|
||||
<xul:label data-l10n-id="security-view-identity-validity"
|
||||
control="security-identity-validity-value"/>
|
||||
</th>
|
||||
<td>
|
||||
<input id="security-identity-validity-value" readonly="readonly"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</groupbox>
|
||||
|
||||
<!-- Privacy & History section -->
|
||||
<groupbox>
|
||||
<label class="header" data-l10n-id="security-view-privacy"/>
|
||||
<grid>
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<!-- History -->
|
||||
<row>
|
||||
<label control="security-privacy-history-value" data-l10n-id="security-view-privacy-history-value"/>
|
||||
<label id="security-privacy-history-value"
|
||||
<table id="securityTable" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!-- History -->
|
||||
<tr>
|
||||
<th>
|
||||
<xul:label control="security-privacy-history-value" data-l10n-id="security-view-privacy-history-value"/>
|
||||
</th>
|
||||
<td>
|
||||
<xul:label id="security-privacy-history-value"
|
||||
data-l10n-id="security-view-unknown"/>
|
||||
</row>
|
||||
<!-- Site Data & Cookies -->
|
||||
<row id="security-privacy-sitedata-row">
|
||||
<label control="security-privacy-sitedata-value" data-l10n-id="security-view-privacy-sitedata-value"/>
|
||||
<hbox id="security-privacy-sitedata-box" align="center">
|
||||
<label id="security-privacy-sitedata-value" data-l10n-id="security-view-unknown"
|
||||
flex="1"/>
|
||||
<button id="security-clear-sitedata"
|
||||
disabled="true"
|
||||
data-l10n-id="security-view-privacy-clearsitedata"
|
||||
oncommand="security.clearSiteData();"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<!-- Passwords -->
|
||||
<row>
|
||||
<label control="security-privacy-passwords-value" data-l10n-id="security-view-privacy-passwords-value"/>
|
||||
<hbox id="security-privacy-passwords-box" align="center">
|
||||
<label id="security-privacy-passwords-value"
|
||||
data-l10n-id="security-view-unknown"
|
||||
flex="1"/>
|
||||
<button id="security-view-password"
|
||||
data-l10n-id="security-view-privacy-viewpasswords"
|
||||
oncommand="security.viewPasswords();"/>
|
||||
</hbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Site Data & Cookies -->
|
||||
<tr id="security-privacy-sitedata-row">
|
||||
<th>
|
||||
<xul:label control="security-privacy-sitedata-value" data-l10n-id="security-view-privacy-sitedata-value"/>
|
||||
</th>
|
||||
<td>
|
||||
<div class="table-split-column">
|
||||
<xul:label id="security-privacy-sitedata-value" data-l10n-id="security-view-unknown"/>
|
||||
<xul:button id="security-clear-sitedata"
|
||||
disabled="true"
|
||||
data-l10n-id="security-view-privacy-clearsitedata"
|
||||
oncommand="security.clearSiteData();"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Passwords -->
|
||||
<tr>
|
||||
<th>
|
||||
<xul:label control="security-privacy-passwords-value" data-l10n-id="security-view-privacy-passwords-value"/>
|
||||
</th>
|
||||
<td>
|
||||
<div class="table-split-column">
|
||||
<xul:label id="security-privacy-passwords-value"
|
||||
data-l10n-id="security-view-unknown"/>
|
||||
<xul:button id="security-view-password"
|
||||
data-l10n-id="security-view-privacy-viewpasswords"
|
||||
oncommand="security.viewPasswords();"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</groupbox>
|
||||
|
||||
<!-- Technical Details section -->
|
||||
|
@ -247,7 +247,7 @@ function securityOnLoad(uri, windowInfo) {
|
||||
// treating these certs as domain-validated only.
|
||||
document.l10n.setAttributes(
|
||||
document.getElementById("security-identity-owner-value"),
|
||||
"security-no-owner"
|
||||
"page-info-security-no-owner"
|
||||
);
|
||||
setText(
|
||||
"security-identity-verifier-value",
|
||||
@ -258,11 +258,11 @@ function securityOnLoad(uri, windowInfo) {
|
||||
// We don't have valid identity credentials.
|
||||
document.l10n.setAttributes(
|
||||
document.getElementById("security-identity-owner-value"),
|
||||
"security-no-owner"
|
||||
"page-info-security-no-owner"
|
||||
);
|
||||
document.l10n.setAttributes(
|
||||
document.getElementById("security-identity-verifier-value"),
|
||||
"not-set-verified-by"
|
||||
"page-info-not-specified"
|
||||
);
|
||||
}
|
||||
|
||||
@ -368,7 +368,7 @@ function setText(id, value) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
if (element.localName == "textbox" || element.localName == "label") {
|
||||
if (element.localName == "input" || element.localName == "label") {
|
||||
element.value = value;
|
||||
} else {
|
||||
element.textContent = value;
|
||||
|
@ -52,10 +52,12 @@
|
||||
}
|
||||
|
||||
let messageManager = window.getGroupMessageManager("browsers");
|
||||
window.messageManager.addMessageListener("contextmenu", this);
|
||||
|
||||
if (gMultiProcessBrowser) {
|
||||
messageManager.addMessageListener("DOMTitleChanged", this);
|
||||
messageManager.addMessageListener("DOMWindowClose", this);
|
||||
window.messageManager.addMessageListener("contextmenu", this);
|
||||
messageManager.addMessageListener("DOMWindowClose", this);
|
||||
messageManager.addMessageListener("Browser:Init", this);
|
||||
} else {
|
||||
this._outerWindowIDBrowserMap.set(
|
||||
@ -1338,9 +1340,7 @@
|
||||
// if the tab is a blank one.
|
||||
if (newBrowser._urlbarFocused && gURLBar) {
|
||||
// Explicitly close the popup if the URL bar retains focus
|
||||
if (!gURLBar.openViewOnFocus) {
|
||||
gURLBar.closePopup();
|
||||
}
|
||||
gURLBar.view.close();
|
||||
|
||||
// If the user happened to type into the URL bar for this browser
|
||||
// by the time we got here, focusing will cause the text to be
|
||||
|
@ -57,7 +57,7 @@
|
||||
frame1.docShell.chromeEventHandler.removeAttribute("crashedPageTitle");
|
||||
|
||||
SimpleTest.is(frame1.contentDocument.documentURI,
|
||||
"about:tabcrashed?e=tabcrashed&u=http%3A//www.example.com/1&c=UTF-8&f=regular&d=pageTitle",
|
||||
"about:tabcrashed?e=tabcrashed&u=http%3A//www.example.com/1&c=UTF-8&d=pageTitle",
|
||||
"Correct about:tabcrashed displayed for page with title.");
|
||||
|
||||
errorPageReady = waitForErrorPage(frame2);
|
||||
@ -66,7 +66,7 @@
|
||||
await errorPageReady;
|
||||
|
||||
SimpleTest.is(frame2.contentDocument.documentURI,
|
||||
"about:tabcrashed?e=tabcrashed&u=http%3A//www.example.com/2&c=UTF-8&f=regular&d=%20",
|
||||
"about:tabcrashed?e=tabcrashed&u=http%3A//www.example.com/2&c=UTF-8&d=%20",
|
||||
"Correct about:tabcrashed displayed for page with no title.");
|
||||
|
||||
SimpleTest.finish();
|
||||
|
@ -55,7 +55,7 @@
|
||||
frame1.docShell.chromeEventHandler.removeAttribute("crashedPageTitle");
|
||||
|
||||
SimpleTest.is(frame1.contentDocument.documentURI,
|
||||
"about:restartrequired?e=restartrequired&u=http%3A//www.example.com/1&c=UTF-8&f=regular&d=%20",
|
||||
"about:restartrequired?e=restartrequired&u=http%3A//www.example.com/1&c=UTF-8&d=%20",
|
||||
"Correct about:restartrequired displayed for page with title.");
|
||||
|
||||
errorPageReady = waitForErrorPage(frame2);
|
||||
@ -64,7 +64,7 @@
|
||||
await errorPageReady;
|
||||
|
||||
SimpleTest.is(frame2.contentDocument.documentURI,
|
||||
"about:restartrequired?e=restartrequired&u=http%3A//www.example.com/2&c=UTF-8&f=regular&d=%20",
|
||||
"about:restartrequired?e=restartrequired&u=http%3A//www.example.com/2&c=UTF-8&d=%20",
|
||||
"Correct about:restartrequired displayed for page with no title.");
|
||||
|
||||
SimpleTest.finish();
|
||||
|
@ -182,6 +182,15 @@ const SELECT_INHERITED_COLORS_ON_OPTIONS_DONT_GET_UNIQUE_RULES_IF_RULE_SET_ON_SE
|
||||
</select></body></html>
|
||||
`;
|
||||
|
||||
const SELECT_FONT_INHERITS_TO_OPTION = `
|
||||
<html><head><style>
|
||||
select { font-family: monospace }
|
||||
</style></head><body><select id='one'>
|
||||
<option>One</option>
|
||||
<option style="font-family: sans-serif">Two</option>
|
||||
</select></body></html>
|
||||
`;
|
||||
|
||||
function getSystemColor(color) {
|
||||
// Need to convert system color to RGB color.
|
||||
let textarea = document.createElementNS(
|
||||
@ -246,7 +255,7 @@ function testOptionColors(index, item, menulist) {
|
||||
}
|
||||
}
|
||||
|
||||
async function testSelectColors(select, itemCount, options) {
|
||||
async function openSelectPopup(select) {
|
||||
const pageUrl = "data:text/html," + escape(select);
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
|
||||
@ -263,7 +272,11 @@ async function testSelectColors(select, itemCount, options) {
|
||||
gBrowser.selectedBrowser
|
||||
);
|
||||
await popupShownPromise;
|
||||
return { tab, menulist, selectPopup };
|
||||
}
|
||||
|
||||
async function testSelectColors(select, itemCount, options) {
|
||||
let { tab, menulist, selectPopup } = await openSelectPopup(select);
|
||||
if (options.waitForComputedStyle) {
|
||||
let property = options.waitForComputedStyle.property;
|
||||
let value = options.waitForComputedStyle.value;
|
||||
@ -624,3 +637,29 @@ add_task(
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(async function test_select_font_inherits_to_option() {
|
||||
let { tab, menulist, selectPopup } = await openSelectPopup(
|
||||
SELECT_FONT_INHERITS_TO_OPTION
|
||||
);
|
||||
|
||||
let popupFont = getComputedStyle(selectPopup).fontFamily;
|
||||
let items = menulist.querySelectorAll("menuitem");
|
||||
is(items.length, 2, "Should have two options");
|
||||
let firstItemFont = getComputedStyle(items[0]).fontFamily;
|
||||
let secondItemFont = getComputedStyle(items[1]).fontFamily;
|
||||
|
||||
is(
|
||||
popupFont,
|
||||
firstItemFont,
|
||||
"First menuitem's font should be inherited from the select"
|
||||
);
|
||||
isnot(
|
||||
popupFont,
|
||||
secondItemFont,
|
||||
"Second menuitem's font should be the author specified one"
|
||||
);
|
||||
|
||||
await hideSelectPopup(selectPopup, "escape");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
@ -69,7 +69,6 @@ support-files =
|
||||
[browser_blob-channelname.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug321000.js]
|
||||
tags = clipboard
|
||||
skip-if = true # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug356571.js]
|
||||
@ -130,7 +129,6 @@ skip-if = true # Bug 1478159
|
||||
[browser_bug533232.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug537013.js]
|
||||
tags = clipboard
|
||||
skip-if = true # bug 1393813
|
||||
# skip-if = e10s # Bug 1134458 - Find bar doesn't work correctly in a detached tab
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
@ -145,10 +143,8 @@ skip-if = true # bug 1393813
|
||||
[browser_bug565575.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug567306.js]
|
||||
tags = clipboard
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug1261299.js]
|
||||
tags = clipboard
|
||||
skip-if = toolkit != "cocoa" # Because of tests for supporting Service Menu of macOS, bug 1261299
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug1297539.js]
|
||||
@ -205,9 +201,6 @@ skip-if = os == "win" && debug && e10s || (verify && debug && (os == 'linux')) #
|
||||
[browser_bug734076.js]
|
||||
skip-if = (verify && debug && (os == 'linux'))
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug735471.js]
|
||||
uses-unsafe-cpows = true
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug749738.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug763468_perwindowpb.js]
|
||||
@ -230,8 +223,6 @@ skip-if = os == 'win' || (verify && debug && (os == 'linux'))
|
||||
[browser_accesskeys.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_clipboard.js]
|
||||
uses-unsafe-cpows = true
|
||||
tags = clipboard
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_clipboard_pastefile.js]
|
||||
skip-if = true # Disabled due to the clipboard not supporting real file types yet (bug 1288773)
|
||||
@ -283,7 +274,6 @@ skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (b
|
||||
[browser_menuButtonFitts.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_middleMouse_noJSPaste.js]
|
||||
tags = clipboard
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_minimize.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
@ -421,7 +411,6 @@ skip-if = debug # Bug 1444565, Bug 1457887
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_blockHPKP.js]
|
||||
skip-if = verify && !debug
|
||||
uses-unsafe-cpows = true
|
||||
tags = psm
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_windowactivation.js]
|
||||
|
@ -27,7 +27,7 @@ add_task(async function() {
|
||||
let contextMenuPromise = BrowserTestUtils.waitForEvent(
|
||||
contextMenu,
|
||||
"popupshown"
|
||||
).then(() => gContextMenuContentData.target);
|
||||
);
|
||||
|
||||
await ContentTask.spawn(
|
||||
tab.linkedBrowser,
|
||||
|
@ -1,26 +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/.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
// Open a new tab.
|
||||
whenNewTabLoaded(window, testPreferences);
|
||||
}
|
||||
|
||||
function testPreferences() {
|
||||
whenTabLoaded(gBrowser.selectedTab, function() {
|
||||
is(
|
||||
content.location.href,
|
||||
"about:preferences",
|
||||
"Checking if the preferences tab was opened"
|
||||
);
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
|
||||
openPreferences();
|
||||
}
|
@ -89,7 +89,7 @@ function createPanel(attrs)
|
||||
button.label = "OK";
|
||||
button.width = 120;
|
||||
button.height = 40;
|
||||
button.setAttribute("style", "-moz-appearance: none; border: 0; margin: 0;");
|
||||
button.setAttribute("style", "appearance: none; border: 0; margin: 0;");
|
||||
panel.setAttribute("style", "-moz-appearance: none; border: 0; margin: 0;");
|
||||
return document.documentElement.appendChild(panel);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ ChromeUtils.defineModuleGetter(
|
||||
);
|
||||
|
||||
const TEST_ORIGIN = "https://example.com";
|
||||
const TEST_HTTP_ORIGIN = "http://example.com";
|
||||
const TEST_SUB_ORIGIN = "https://test1.example.com";
|
||||
const REMOVE_DIALOG_URL =
|
||||
"chrome://browser/content/preferences/siteDataRemoveSelected.xul";
|
||||
@ -36,30 +37,67 @@ add_task(async function test_CertificateError() {
|
||||
|
||||
let pageInfo = BrowserPageInfo(TEST_ORIGIN_CERT_ERROR, "securityTab");
|
||||
await BrowserTestUtils.waitForEvent(pageInfo, "load");
|
||||
let securityTab = pageInfo.document.getElementById("securityTab");
|
||||
let pageInfoDoc = pageInfo.document;
|
||||
let securityTab = pageInfoDoc.getElementById("securityTab");
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() => BrowserTestUtils.is_visible(securityTab),
|
||||
"Security tab should be visible."
|
||||
);
|
||||
|
||||
let owner = pageInfo.document.getElementById("security-identity-owner-value");
|
||||
let verifier = pageInfo.document.getElementById(
|
||||
"security-identity-verifier-value"
|
||||
);
|
||||
let domain = pageInfo.document.getElementById(
|
||||
"security-identity-domain-value"
|
||||
let owner = pageInfoDoc.getElementById("security-identity-owner-value");
|
||||
let verifier = pageInfoDoc.getElementById("security-identity-verifier-value");
|
||||
let domain = pageInfoDoc.getElementById("security-identity-domain-value");
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() => owner.value === "This website does not supply ownership information.",
|
||||
`Value of owner should be should be "This website does not supply ownership information." instead got "${
|
||||
verifier.value
|
||||
}".`
|
||||
);
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() =>
|
||||
owner.textContent ===
|
||||
"This website does not supply ownership information.",
|
||||
`Value of owner should be should be "This website does not supply ownership information." instead got "${
|
||||
() => verifier.textContent === "Not specified",
|
||||
`Value of verifier should be "Mozilla Testing", instead got "${
|
||||
verifier.textContent
|
||||
}".`
|
||||
);
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() => domain.value === browser.currentURI.displayHost,
|
||||
`Value of domain should be ${
|
||||
browser.currentURI.displayHost
|
||||
}, instead got "${domain.value}".`
|
||||
);
|
||||
|
||||
pageInfo.close();
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
// Test displaying website identity information on http pages.
|
||||
add_task(async function test_SecurityHTTP() {
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_HTTP_ORIGIN);
|
||||
|
||||
let pageInfo = BrowserPageInfo(TEST_HTTP_ORIGIN, "securityTab");
|
||||
await BrowserTestUtils.waitForEvent(pageInfo, "load");
|
||||
let pageInfoDoc = pageInfo.document;
|
||||
let securityTab = pageInfoDoc.getElementById("securityTab");
|
||||
await TestUtils.waitForCondition(
|
||||
() => BrowserTestUtils.is_visible(securityTab),
|
||||
"Security tab should be visible."
|
||||
);
|
||||
|
||||
let owner = pageInfoDoc.getElementById("security-identity-owner-value");
|
||||
let verifier = pageInfoDoc.getElementById("security-identity-verifier-value");
|
||||
let domain = pageInfoDoc.getElementById("security-identity-domain-value");
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() => owner.value === "This website does not supply ownership information.",
|
||||
`Value of owner should be should be "This website does not supply ownership information." instead got "${
|
||||
verifier.value
|
||||
}".`
|
||||
);
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() => verifier.textContent === "Not specified",
|
||||
`Value of verifier should be "Not specified", instead got "${
|
||||
@ -68,9 +106,9 @@ add_task(async function test_CertificateError() {
|
||||
);
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() => domain.value === browser.currentURI.displayHost,
|
||||
() => domain.value === gBrowser.selectedBrowser.currentURI.displayHost,
|
||||
`Value of domain should be ${
|
||||
browser.currentURI.displayHost
|
||||
gBrowser.selectedBrowser.currentURI.displayHost
|
||||
}, instead got "${domain.value}".`
|
||||
);
|
||||
|
||||
@ -88,13 +126,10 @@ add_task(async function test_SiteData() {
|
||||
|
||||
let pageInfo = BrowserPageInfo(TEST_ORIGIN, "securityTab");
|
||||
await BrowserTestUtils.waitForEvent(pageInfo, "load");
|
||||
let pageInfoDoc = pageInfo.document;
|
||||
|
||||
let label = pageInfo.document.getElementById(
|
||||
"security-privacy-sitedata-value"
|
||||
);
|
||||
let clearButton = pageInfo.document.getElementById(
|
||||
"security-clear-sitedata"
|
||||
);
|
||||
let label = pageInfoDoc.getElementById("security-privacy-sitedata-value");
|
||||
let clearButton = pageInfoDoc.getElementById("security-clear-sitedata");
|
||||
|
||||
let size = DownloadUtils.convertByteUnits(totalUsage);
|
||||
|
||||
@ -142,12 +177,10 @@ add_task(async function test_Cookies() {
|
||||
let pageInfo = BrowserPageInfo(TEST_ORIGIN, "securityTab");
|
||||
await BrowserTestUtils.waitForEvent(pageInfo, "load");
|
||||
|
||||
let label = pageInfo.document.getElementById(
|
||||
"security-privacy-sitedata-value"
|
||||
);
|
||||
let clearButton = pageInfo.document.getElementById(
|
||||
"security-clear-sitedata"
|
||||
);
|
||||
let pageInfoDoc = pageInfo.document;
|
||||
|
||||
let label = pageInfoDoc.getElementById("security-privacy-sitedata-value");
|
||||
let clearButton = pageInfoDoc.getElementById("security-clear-sitedata");
|
||||
|
||||
// The usage details are filled asynchronously, so we assert that they're present by
|
||||
// waiting for them to be filled in.
|
||||
|
@ -1,116 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
// This tests searching in the legacy urlbar implementation (a.k.a. the
|
||||
// awesomebar).
|
||||
|
||||
// There are a _lot_ of reflows in this test, and processing them takes
|
||||
// time. On slower builds, we need to boost our allowed test time.
|
||||
requestLongerTimeout(5);
|
||||
|
||||
/**
|
||||
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
|
||||
* is a whitelist that should slowly go away as we improve the performance of
|
||||
* the front-end. Instead of adding more reflows to the whitelist, you should
|
||||
* be modifying your code to avoid the reflow.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
|
||||
* for tips on how to do that.
|
||||
*/
|
||||
|
||||
/* These reflows happen only the first time the awesomebar panel opens. */
|
||||
const EXPECTED_REFLOWS_FIRST_OPEN = [];
|
||||
if (
|
||||
AppConstants.platform == "linux" ||
|
||||
AppConstants.platform == "win" ||
|
||||
// macOS 10.14 Mojave (Darwin version 18)
|
||||
AppConstants.isPlatformAndVersionAtLeast("macosx", "18")
|
||||
) {
|
||||
EXPECTED_REFLOWS_FIRST_OPEN.push({
|
||||
stack: [
|
||||
"__rebuild@chrome://browser/content/search/search-one-offs.js",
|
||||
/* This is limited to a one-line stack, because the next item is an async
|
||||
function and as such not supported on all trees, according to bug 1501761.
|
||||
"async*set popup@chrome://browser/content/search/search-one-offs.js",
|
||||
"_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
|
||||
"toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml",
|
||||
"_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
|
||||
"@chrome://browser/content/urlbarBindings.xml",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/
|
||||
],
|
||||
});
|
||||
}
|
||||
EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
maxCount: 60, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
maxCount: 6, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
// Bug 1359989
|
||||
{
|
||||
stack: [
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// These extra reflows happen on beta/release as one of the default bookmarks in
|
||||
// bookmarks.html.in has a long URL.
|
||||
if (AppConstants.RELEASE_OR_BETA) {
|
||||
EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_onUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
],
|
||||
maxCount: 6,
|
||||
},
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_onOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
],
|
||||
maxCount: 6,
|
||||
},
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_adjustAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
maxCount: 12,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function awesomebar() {
|
||||
await runUrlbarTest(true, true, EXPECTED_REFLOWS_FIRST_OPEN);
|
||||
});
|
@ -1,113 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
// This tests searching in the legacy urlbar implementation (a.k.a. the
|
||||
// awesomebar).
|
||||
|
||||
// There are a _lot_ of reflows in this test, and processing them takes
|
||||
// time. On slower builds, we need to boost our allowed test time.
|
||||
requestLongerTimeout(5);
|
||||
|
||||
/**
|
||||
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
|
||||
* is a whitelist that should slowly go away as we improve the performance of
|
||||
* the front-end. Instead of adding more reflows to the whitelist, you should
|
||||
* be modifying your code to avoid the reflow.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
|
||||
* for tips on how to do that.
|
||||
*/
|
||||
|
||||
/* These reflows happen only the first time the awesomebar panel opens. */
|
||||
const EXPECTED_REFLOWS_FIRST_OPEN = [];
|
||||
if (
|
||||
AppConstants.platform == "linux" ||
|
||||
AppConstants.platform == "win" ||
|
||||
// macOS 10.14 Mojave (Darwin version 18)
|
||||
AppConstants.isPlatformAndVersionAtLeast("macosx", "18")
|
||||
) {
|
||||
EXPECTED_REFLOWS_FIRST_OPEN.push({
|
||||
stack: [
|
||||
"__rebuild@chrome://browser/content/search/search-one-offs.js",
|
||||
/* This is limited to a one-line stack, because the next item is an async
|
||||
function and as such not supported on all trees, according to bug 1501761.
|
||||
"async*set popup@chrome://browser/content/search/search-one-offs.js",
|
||||
"_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
|
||||
"toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml",
|
||||
"_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
|
||||
"@chrome://browser/content/urlbarBindings.xml",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/
|
||||
],
|
||||
});
|
||||
}
|
||||
EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
maxCount: 36, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
maxCount: 6, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
// Bug 1359989
|
||||
{
|
||||
stack: [
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
/* These reflows happen everytime the awesomebar panel opens. */
|
||||
const EXPECTED_REFLOWS_SECOND_OPEN = [
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
maxCount: 24, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
// Bug 1359989
|
||||
{
|
||||
stack: [
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
add_task(async function awesomebar() {
|
||||
await runUrlbarTest(
|
||||
true,
|
||||
false,
|
||||
EXPECTED_REFLOWS_FIRST_OPEN,
|
||||
EXPECTED_REFLOWS_SECOND_OPEN
|
||||
);
|
||||
});
|
@ -1,8 +0,0 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
browser.urlbar.quantumbar=false
|
||||
|
||||
[../browser_urlbar_keyed_search_legacy.js]
|
||||
skip-if = (os == 'linux') || (os == 'win' && debug) || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug due to perma failures. Bug 1392320. Disabled on Win32 because of intermittent OOM failures (bug 1448241).
|
||||
[../browser_urlbar_search_legacy.js]
|
||||
skip-if = (debug || ccov) && (os == 'linux' || os == 'win') || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug and ccov due to intermittent timeouts. Bug 1414126, bug 1426611. Disabled on Win32 because of intermittent OOM failures (bug 1448241)
|
@ -43,7 +43,6 @@ add_task(async function() {
|
||||
"plugin should not have been found."
|
||||
);
|
||||
|
||||
// simple cpows
|
||||
await ContentTask.spawn(gTestBrowser, null, function() {
|
||||
let plugin = content.document.getElementById("plugin");
|
||||
ok(plugin, "plugin should be in the page");
|
||||
|
@ -235,9 +235,6 @@ const ignorableWhitelist = new Set([
|
||||
// toolkit/mozapps/extensions/nsBlocklistService.js
|
||||
"resource://app/blocklist.xml",
|
||||
|
||||
// dom/media/gmp/GMPParent.cpp
|
||||
"resource://gre/gmp-clearkey/0.1/manifest.json",
|
||||
|
||||
// Bug 1351669 - obsolete test file
|
||||
"resource://gre/res/test.properties",
|
||||
|
||||
|
@ -35,7 +35,7 @@ let whitelist = [
|
||||
isFromDevTools: false,
|
||||
},
|
||||
{
|
||||
sourceName: /\b(minimal-xul|html|mathml|ua)\.css$/i,
|
||||
sourceName: /\b(minimal-xul|html|mathml|ua|forms|svg)\.css$/i,
|
||||
errorMessage: /Unknown property.*-moz-/i,
|
||||
isFromDevTools: false,
|
||||
},
|
||||
@ -57,14 +57,6 @@ let whitelist = [
|
||||
platforms: ["linux"],
|
||||
isFromDevTools: false,
|
||||
},
|
||||
// The '-moz-menulist-arrow-button' value is only supported in chrome and UA sheets
|
||||
// but forms.css is loaded as a document sheet by this test.
|
||||
// Maybe bug 1261237 will fix this?
|
||||
{
|
||||
sourceName: /(?:res|gre-resources)\/forms\.css$/i,
|
||||
errorMessage: /Error in parsing value for \u2018-moz-appearance\u2019/iu,
|
||||
isFromDevTools: false,
|
||||
},
|
||||
// These variables are declared somewhere else, and error when we load the
|
||||
// files directly. They're all marked intermittent because their appearance
|
||||
// in the error console seems to not be consistent.
|
||||
@ -101,14 +93,6 @@ if (
|
||||
});
|
||||
}
|
||||
|
||||
if (!Services.prefs.getBoolPref("layout.css.scrollbar-width.enabled")) {
|
||||
whitelist.push({
|
||||
sourceName: /(?:res|gre-resources)\/forms\.css$/i,
|
||||
errorMessage: /Unknown property .*\bscrollbar-width\b/i,
|
||||
isFromDevTools: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (!Services.prefs.getBoolPref("layout.css.file-chooser-button.enabled")) {
|
||||
// Reserved to UA sheets, behind a pref for content.
|
||||
whitelist.push({
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -364,7 +364,7 @@ function openLinkIn(url, where, params) {
|
||||
var aPostData = params.postData;
|
||||
var aCharset = params.charset;
|
||||
var aReferrerInfo = params.referrerInfo
|
||||
? params.referrerInfo
|
||||
? E10SUtils.deserializeReferrerInfo(params.referrerInfo)
|
||||
: new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, null);
|
||||
var aRelatedToCurrent = params.relatedToCurrent;
|
||||
var aAllowInheritPrincipal = !!params.allowInheritPrincipal;
|
||||
@ -390,8 +390,6 @@ function openLinkIn(url, where, params) {
|
||||
}
|
||||
|
||||
if (where == "save") {
|
||||
// TODO(1073187): propagate referrerPolicy.
|
||||
// ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
|
||||
if ("isContentWindowPrivate" in params) {
|
||||
saveURL(
|
||||
url,
|
||||
@ -1066,12 +1064,6 @@ function openFeedbackPage() {
|
||||
openTrustedLinkIn(url, "tab");
|
||||
}
|
||||
|
||||
function openTourPage() {
|
||||
let scope = {};
|
||||
ChromeUtils.import("resource:///modules/UITour.jsm", scope);
|
||||
openTrustedLinkIn(scope.UITour.url, "tab");
|
||||
}
|
||||
|
||||
function buildHelpMenu() {
|
||||
document.getElementById(
|
||||
"feedbackPage"
|
||||
|
@ -93,7 +93,7 @@ browser.jar:
|
||||
content/browser/tabbrowser.css (content/tabbrowser.css)
|
||||
content/browser/tabbrowser.js (content/tabbrowser.js)
|
||||
content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||
* content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||
content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||
content/browser/utilityOverlay.js (content/utilityOverlay.js)
|
||||
content/browser/webext-panels.js (content/webext-panels.js)
|
||||
* content/browser/webext-panels.xul (content/webext-panels.xul)
|
||||
|
@ -33,7 +33,6 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
'content/test/performance/browser.ini',
|
||||
'content/test/performance/hidpi/browser.ini',
|
||||
'content/test/performance/io/browser.ini',
|
||||
'content/test/performance/legacyurlbar/browser.ini',
|
||||
'content/test/performance/lowdpi/browser.ini',
|
||||
'content/test/permissions/browser.ini',
|
||||
'content/test/plugins/browser-rs-blocklist.ini',
|
||||
|
@ -42,6 +42,7 @@ let LEGACY_ACTORS = {
|
||||
matches: ["about:logins", "about:logins?*"],
|
||||
module: "resource:///actors/AboutLoginsChild.jsm",
|
||||
events: {
|
||||
AboutLoginsCopyLoginDetail: { wantUntrusted: true },
|
||||
AboutLoginsCreateLogin: { wantUntrusted: true },
|
||||
AboutLoginsDeleteLogin: { wantUntrusted: true },
|
||||
AboutLoginsImport: { wantUntrusted: true },
|
||||
@ -55,6 +56,7 @@ let LEGACY_ACTORS = {
|
||||
"AboutLogins:LoginAdded",
|
||||
"AboutLogins:LoginModified",
|
||||
"AboutLogins:LoginRemoved",
|
||||
"AboutLogins:MasterPasswordResponse",
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -284,16 +286,6 @@ let LEGACY_ACTORS = {
|
||||
},
|
||||
},
|
||||
|
||||
UITour: {
|
||||
child: {
|
||||
module: "resource:///modules/UITourChild.jsm",
|
||||
events: {
|
||||
mozUITour: { wantUntrusted: true },
|
||||
},
|
||||
permissions: ["uitour"],
|
||||
},
|
||||
},
|
||||
|
||||
URIFixup: {
|
||||
child: {
|
||||
module: "resource:///actors/URIFixupChild.jsm",
|
||||
@ -453,7 +445,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
PluralForm: "resource://gre/modules/PluralForm.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.js",
|
||||
RemoteSecuritySettings: "resource://gre/modules/psm/RemoteSecuritySettings.jsm",
|
||||
RFPHelper: "resource://gre/modules/RFPHelper.jsm",
|
||||
SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
|
||||
@ -465,7 +456,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
|
||||
TabUnloader: "resource:///modules/TabUnloader.jsm",
|
||||
UIState: "resource://services-sync/UIState.jsm",
|
||||
UITour: "resource:///modules/UITour.jsm",
|
||||
WebChannel: "resource://gre/modules/WebChannel.jsm",
|
||||
WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm",
|
||||
});
|
||||
@ -561,6 +551,7 @@ const listeners = {
|
||||
"AboutLogins:CreateLogin": ["AboutLoginsParent"],
|
||||
"AboutLogins:DeleteLogin": ["AboutLoginsParent"],
|
||||
"AboutLogins:Import": ["AboutLoginsParent"],
|
||||
"AboutLogins:MasterPasswordRequest": ["AboutLoginsParent"],
|
||||
"AboutLogins:OpenPreferences": ["AboutLoginsParent"],
|
||||
"AboutLogins:OpenSite": ["AboutLoginsParent"],
|
||||
"AboutLogins:Subscribe": ["AboutLoginsParent"],
|
||||
@ -1959,14 +1950,6 @@ BrowserGlue.prototype = {
|
||||
// can check the log.
|
||||
this._gmpInstallManager.simpleCheckAndInstall().catch(() => {});
|
||||
});
|
||||
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
RemoteSettings.init();
|
||||
});
|
||||
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
RemoteSecuritySettings.init();
|
||||
});
|
||||
},
|
||||
|
||||
_onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
|
||||
@ -2011,10 +1994,6 @@ BrowserGlue.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
if (pagecount < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aQuitType) {
|
||||
aQuitType = "quit";
|
||||
}
|
||||
@ -2067,10 +2046,12 @@ BrowserGlue.prototype = {
|
||||
? "tabs.closeWarningMultipleSessionRestore2"
|
||||
: "tabs.closeWarningMultiple";
|
||||
warningMessage = gTabbrowserBundle.GetStringFromName(stringID);
|
||||
warningMessage = PluralForm.get(pagecount, warningMessage).replace(
|
||||
"#1",
|
||||
pagecount
|
||||
);
|
||||
if (pagecount > 1) {
|
||||
warningMessage = PluralForm.get(pagecount, warningMessage).replace(
|
||||
"#1",
|
||||
pagecount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let warnOnClose = { value: true };
|
||||
@ -4063,13 +4044,6 @@ var JawsScreenReaderVersionCheck = {
|
||||
},
|
||||
};
|
||||
|
||||
// Listen for UITour messages.
|
||||
// Do it here instead of the UITour module itself so that the UITour module is lazy loaded
|
||||
// when the first message is received.
|
||||
Services.mm.addMessageListener("UITour:onPageEvent", function(aMessage) {
|
||||
UITour.onPageEvent(aMessage, aMessage.data);
|
||||
});
|
||||
|
||||
// Listen for HybridContentTelemetry messages.
|
||||
// Do it here instead of HybridContentTelemetry.init() so that
|
||||
// the module can be lazily loaded on the first message.
|
||||
|
@ -134,7 +134,7 @@ body.config-warning {
|
||||
|
||||
td.cell-value > form > input[type="text"],
|
||||
td.cell-value > form > input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
@ -12,6 +12,9 @@ const { ActorChild } = ChromeUtils.import(
|
||||
const { LoginHelper } = ChromeUtils.import(
|
||||
"resource://gre/modules/LoginHelper.jsm"
|
||||
);
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
@ -19,11 +22,21 @@ ChromeUtils.defineModuleGetter(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"ClipboardHelper",
|
||||
"@mozilla.org/widget/clipboardhelper;1",
|
||||
"nsIClipboardHelper"
|
||||
);
|
||||
|
||||
let masterPasswordPromise;
|
||||
|
||||
class AboutLoginsChild extends ActorChild {
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "AboutLoginsInit": {
|
||||
this.mm.sendAsyncMessage("AboutLogins:Subscribe");
|
||||
let messageManager = this.mm;
|
||||
messageManager.sendAsyncMessage("AboutLogins:Subscribe");
|
||||
|
||||
let documentElement = this.content.document.documentElement;
|
||||
documentElement.classList.toggle(
|
||||
@ -36,6 +49,15 @@ class AboutLoginsChild extends ActorChild {
|
||||
doLoginsMatch(loginA, loginB) {
|
||||
return LoginHelper.doLoginsMatch(loginA, loginB, {});
|
||||
},
|
||||
promptForMasterPassword(resolve) {
|
||||
masterPasswordPromise = {
|
||||
resolve,
|
||||
};
|
||||
|
||||
messageManager.sendAsyncMessage(
|
||||
"AboutLogins:MasterPasswordRequest"
|
||||
);
|
||||
},
|
||||
};
|
||||
waivedContent.AboutLoginsUtils = Cu.cloneInto(
|
||||
AboutLoginsUtils,
|
||||
@ -46,6 +68,10 @@ class AboutLoginsChild extends ActorChild {
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsCopyLoginDetail": {
|
||||
ClipboardHelper.copyString(event.detail);
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsCreateLogin": {
|
||||
this.mm.sendAsyncMessage("AboutLogins:CreateLogin", {
|
||||
login: event.detail,
|
||||
@ -95,6 +121,11 @@ class AboutLoginsChild extends ActorChild {
|
||||
case "AboutLogins:LoginRemoved":
|
||||
this.sendToContent("LoginRemoved", message.data);
|
||||
break;
|
||||
case "AboutLogins:MasterPasswordResponse":
|
||||
if (masterPasswordPromise) {
|
||||
masterPasswordPromise.resolve(message.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,18 +37,9 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
const ABOUT_LOGINS_ORIGIN = "about:logins";
|
||||
const MASTER_PASSWORD_NOTIFICATION_ID = "master-password-login-required";
|
||||
|
||||
const PRIVILEGED_PROCESS_PREF =
|
||||
"browser.tabs.remote.separatePrivilegedContentProcess";
|
||||
const PRIVILEGED_PROCESS_ENABLED = Services.prefs.getBoolPref(
|
||||
PRIVILEGED_PROCESS_PREF,
|
||||
false
|
||||
);
|
||||
|
||||
// When the privileged content process is enabled, we expect about:logins
|
||||
// to load in it. Otherwise, it's in a normal web content process.
|
||||
const EXPECTED_ABOUTLOGINS_REMOTE_TYPE = PRIVILEGED_PROCESS_ENABLED
|
||||
? E10SUtils.PRIVILEGED_REMOTE_TYPE
|
||||
: E10SUtils.DEFAULT_REMOTE_TYPE;
|
||||
// about:logins will always use the privileged content process,
|
||||
// even if it is disabled for other consumers such as about:newtab.
|
||||
const EXPECTED_ABOUTLOGINS_REMOTE_TYPE = E10SUtils.PRIVILEGED_REMOTE_TYPE;
|
||||
|
||||
const isValidLogin = login => {
|
||||
return !(login.origin || "").startsWith("chrome://");
|
||||
@ -148,6 +139,50 @@ var AboutLoginsParent = {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "AboutLogins:MasterPasswordRequest": {
|
||||
// This doesn't harm if passwords are not encrypted
|
||||
let tokendb = Cc["@mozilla.org/security/pk11tokendb;1"].createInstance(
|
||||
Ci.nsIPK11TokenDB
|
||||
);
|
||||
let token = tokendb.getInternalKeyToken();
|
||||
|
||||
let messageManager = message.target.messageManager;
|
||||
|
||||
// If there is no master password, return as-if authentication succeeded.
|
||||
if (token.checkPassword("")) {
|
||||
messageManager.sendAsyncMessage(
|
||||
"AboutLogins:MasterPasswordResponse",
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a master password prompt is already open, just exit early and return false.
|
||||
// The user can re-trigger it after responding to the already open dialog.
|
||||
if (Services.logins.uiBusy) {
|
||||
messageManager.sendAsyncMessage(
|
||||
"AboutLogins:MasterPasswordResponse",
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// So there's a master password. But since checkPassword didn't succeed, we're logged out (per nsIPK11Token.idl).
|
||||
try {
|
||||
// Relogin and ask for the master password.
|
||||
token.login(true); // 'true' means always prompt for token password. User will be prompted until
|
||||
// clicking 'Cancel' or entering the correct password.
|
||||
} catch (e) {
|
||||
// An exception will be thrown if the user cancels the login prompt dialog.
|
||||
// User is also logged out of Software Security Device.
|
||||
}
|
||||
|
||||
messageManager.sendAsyncMessage(
|
||||
"AboutLogins:MasterPasswordResponse",
|
||||
token.isLoggedIn()
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "AboutLogins:Subscribe": {
|
||||
if (
|
||||
!ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers).length
|
||||
|
@ -3,8 +3,9 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
body {
|
||||
--sidebar-width: 320px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(320px, max-content) 1fr;
|
||||
grid-template-columns: var(--sidebar-width) 1fr;
|
||||
grid-template-rows: 75px 1fr;
|
||||
grid-template-areas: "header header"
|
||||
"logins login";
|
||||
@ -15,13 +16,16 @@ header {
|
||||
display: flex;
|
||||
grid-area: header;
|
||||
align-items: center;
|
||||
background-color: var(--in-content-box-background);
|
||||
background-color: var(--in-content-page-background);
|
||||
border-bottom: 1px solid var(--in-content-box-border-color);
|
||||
padding: 0 18px;
|
||||
padding-inline-end: 23px;
|
||||
}
|
||||
|
||||
login-filter {
|
||||
flex: auto;
|
||||
min-width: 320px;
|
||||
max-width: 400px;
|
||||
margin-inline: 40px auto;
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
@ -38,12 +42,3 @@ login-item {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
#branding-logo {
|
||||
height: 32px;
|
||||
margin-inline-end: 18px;
|
||||
}
|
||||
|
||||
:root:not(.official-branding) #branding-logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; img-src data: blob:;"/>
|
||||
<title data-l10n-id="about-logins-page-title"></title>
|
||||
<link rel="localization" href="preview/aboutLogins.ftl">
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/confirm-delete-dialog.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/login-filter.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/login-item.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/login-list.js"></script>
|
||||
@ -28,24 +28,25 @@
|
||||
</header>
|
||||
<login-list></login-list>
|
||||
<login-item></login-item>
|
||||
<confirm-delete-dialog hidden></confirm-delete-dialog>
|
||||
<confirmation-dialog hidden></confirmation-dialog>
|
||||
|
||||
<template id="confirm-delete-dialog-template">
|
||||
<template id="confirmation-dialog-template">
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/confirm-delete-dialog.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/confirmation-dialog.css">
|
||||
<div class="overlay">
|
||||
<div class="container" role="dialog" aria-labelledby="title" aria-describedby="message">
|
||||
<div class="title-bar">
|
||||
<h1 class="title" id="title" data-l10n-id="confirm-delete-dialog-title"></h1>
|
||||
<button class="dismiss-button" data-l10n-id="confirm-delete-dialog-dismiss-button"></button>
|
||||
</div>
|
||||
<button class="dismiss-button" data-l10n-id="confirmation-dialog-dismiss-button">
|
||||
<img class="dismiss-icon" src="chrome://global/skin/icons/close.svg"/>
|
||||
</button>
|
||||
<div class="content">
|
||||
<p class="message" id="message" data-l10n-id="confirm-delete-dialog-message"></p>
|
||||
<img class="warning-icon" src="chrome://global/skin/icons/warning.svg"/>
|
||||
<h1 class="title" id="title"></h1>
|
||||
<p class="message" id="message"></p>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button class="cancel-button" data-l10n-id="confirm-delete-dialog-cancel-button"></button>
|
||||
<button class="confirm-button" data-l10n-id="confirm-delete-dialog-confirm-button"></button>
|
||||
<button class="confirm-button danger-button"></button>
|
||||
<button class="cancel-button" data-l10n-id="confirmation-dialog-cancel-button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,7 +73,7 @@
|
||||
</template>
|
||||
|
||||
<template id="login-list-item-template">
|
||||
<li class="login-list-item">
|
||||
<li class="labels login-list-item" role="option">
|
||||
<span class="title"></span>
|
||||
<span class="username"></span>
|
||||
</li>
|
||||
@ -87,23 +88,23 @@
|
||||
<span class="login-item-title"></span>
|
||||
<span class="new-login-title" data-l10n-id="login-item-new-login-title"></span>
|
||||
</h2>
|
||||
<button class="edit-button alternate-button" data-l10n-id="login-item-edit-button"></button>
|
||||
<button class="delete-button alternate-button" data-l10n-id="login-item-delete-button"></button>
|
||||
<button class="edit-button ghost-button" data-l10n-id="login-item-edit-button"></button>
|
||||
<button class="delete-button ghost-button" data-l10n-id="login-item-delete-button"></button>
|
||||
</div>
|
||||
<form>
|
||||
<div class="detail-row">
|
||||
<label class="detail-cell">
|
||||
<span class="origin-label field-label" data-l10n-id="login-item-origin-label"></span>
|
||||
<input type="url" name="origin" class="origin-input" required data-l10n-id="login-item-origin"/>
|
||||
<input type="url" name="origin" class="origin-input" required data-l10n-id="login-item-origin" dir="auto"/>
|
||||
</label>
|
||||
<button class="open-site-button" data-l10n-id="login-item-open-site-button"></button>
|
||||
<button class="open-site-button" data-l10n-id="login-item-open-site-button" type="button"></button>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<label class="detail-cell">
|
||||
<span class="username-label field-label" data-l10n-id="login-item-username-label"></span>
|
||||
<input type="text" name="username" data-l10n-id="login-item-username"/>
|
||||
</label>
|
||||
<button class="copy-button copy-username-button" data-copy-login-property="username">
|
||||
<button class="copy-button copy-username-button" data-copy-login-property="username" type="button">
|
||||
<span class="copied-button-text" data-l10n-id="login-item-copied-username-button-text"></span>
|
||||
<span class="copy-button-text" data-l10n-id="login-item-copy-username-button-text"></span>
|
||||
</button>
|
||||
@ -118,7 +119,7 @@
|
||||
data-l10n-id="login-item-password-reveal-checkbox"/>
|
||||
</div>
|
||||
</label>
|
||||
<button class="copy-button copy-password-button" data-copy-login-property="password">
|
||||
<button class="copy-button copy-password-button" data-copy-login-property="password" type="button">
|
||||
<span class="copied-button-text" data-l10n-id="login-item-copied-password-button-text"></span>
|
||||
<span class="copy-button-text" data-l10n-id="login-item-copy-password-button-text"></span>
|
||||
</button>
|
||||
@ -126,8 +127,8 @@
|
||||
<p class="time-created meta-info" data-l10n-id="login-item-time-created" data-l10n-args='{"timeCreated": 0}'></p>
|
||||
<p class="time-changed meta-info" data-l10n-id="login-item-time-changed" data-l10n-args='{"timeChanged": 0}'></p>
|
||||
<p class="time-used meta-info" data-l10n-id="login-item-time-used" data-l10n-args='{"timeUsed": 0}'></p>
|
||||
<button class="save-changes-button" data-l10n-id="login-item-save-changes-button"></button>
|
||||
<button class="cancel-button" data-l10n-id="login-item-cancel-button"></button>
|
||||
<button class="save-changes-button" type="submit"></button>
|
||||
<button class="cancel-button" data-l10n-id="login-item-cancel-button" type="button"></button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
@ -135,17 +136,17 @@
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/login-filter.css">
|
||||
<input data-l10n-id="login-filter" class="filter" type="text"/>
|
||||
<input data-l10n-id="login-filter" class="filter" type="text" dir="auto"/>
|
||||
</template>
|
||||
|
||||
<template id="menu-button-template">
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/common.css">
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/menu-button.css">
|
||||
<button class="menu-button alternate-button" data-l10n-id="menu"></button>
|
||||
<button class="menu-button ghost-button" data-l10n-id="menu"></button>
|
||||
<ul class="menu" role="menu" hidden>
|
||||
<button role="menuitem" class="menuitem-button menuitem-import alternate-button" hidden data-supported-platforms="Win32" data-event-name="AboutLoginsImport" data-l10n-id="menu-menuitem-import"></button>
|
||||
<button role="menuitem" class="menuitem-button menuitem-preferences alternate-button" data-event-name="AboutLoginsOpenPreferences" data-l10n-id="menu-menuitem-preferences"></button>
|
||||
<button role="menuitem" class="menuitem-button menuitem-import ghost-button" hidden data-supported-platforms="Win32" data-event-name="AboutLoginsImport" data-l10n-id="menu-menuitem-import"></button>
|
||||
<button role="menuitem" class="menuitem-button menuitem-preferences ghost-button" data-event-name="AboutLoginsOpenPreferences" data-l10n-id="menu-menuitem-preferences"></button>
|
||||
</ul>
|
||||
</template>
|
||||
</body>
|
||||
|
@ -7,15 +7,3 @@
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.alternate-button {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.alternate-button:hover {
|
||||
background-color: var(--in-content-button-background-hover);
|
||||
}
|
||||
|
||||
.alternate-button:hover:active {
|
||||
background-color: var(--in-content-button-background-active);
|
||||
}
|
||||
|
@ -1,88 +1,85 @@
|
||||
.overlay {
|
||||
/* 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/. */
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
inset: 0;
|
||||
/* TODO: this color is used in the about:preferences overlay, but
|
||||
why isn't it declared as a variable? */
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
z-index: 2;
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
width: 50%;
|
||||
min-width: 250px;
|
||||
max-width: 500px;
|
||||
height: 40%;
|
||||
min-height: 200px;
|
||||
margin: auto;
|
||||
background: var(--in-content-page-background);
|
||||
color: var(--in-content-page-color);
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
position: relative;
|
||||
flex: 0 1 auto;
|
||||
text-align: center;
|
||||
background-color: var(--in-content-dialog-header-background);
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid var(--in-content-border-color);
|
||||
box-shadow: var(--shadow-30);
|
||||
/* show a border in high contrast mode */
|
||||
outline: 1px solid transparent;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: .9em;
|
||||
line-height: 1.8em;
|
||||
font-weight: 600;
|
||||
font-size: 1.5em;
|
||||
font-weight: normal;
|
||||
-moz-user-select: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button.dismiss-button {
|
||||
.message {
|
||||
color: var(--in-content-deemphasized-text);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dismiss-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
inset-inline-end: 0;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
margin: 8px 16px;
|
||||
margin: 16px;
|
||||
padding: 0;
|
||||
background: url(chrome://global/skin/icons/close.svg) no-repeat center;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.dismiss-icon {
|
||||
-moz-context-properties: fill, fill-opacity;
|
||||
fill: currentColor;
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
button.dismiss-button:dir(rtl) {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.buttons button {
|
||||
margin: 0;
|
||||
.warning-icon {
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.content,
|
||||
.buttons {
|
||||
margin: 16px;
|
||||
text-align: center;
|
||||
padding: 16px 32px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.buttons.macosx > .confirm-button {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.buttons > button {
|
||||
min-width: 140px;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
export default class ConfirmDeleteDialog extends HTMLElement {
|
||||
export default class ConfirmationDialog extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this._promise = null;
|
||||
@ -12,22 +12,31 @@ export default class ConfirmDeleteDialog extends HTMLElement {
|
||||
if (this.shadowRoot) {
|
||||
return;
|
||||
}
|
||||
let template = document.querySelector("#confirm-delete-dialog-template");
|
||||
let template = document.querySelector("#confirmation-dialog-template");
|
||||
let shadowRoot = this.attachShadow({ mode: "open" });
|
||||
document.l10n.connectRoot(shadowRoot);
|
||||
shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
|
||||
this._buttons = this.shadowRoot.querySelector(".buttons");
|
||||
this._cancelButton = this.shadowRoot.querySelector(".cancel-button");
|
||||
this._confirmButton = this.shadowRoot.querySelector(".confirm-button");
|
||||
this._dismissButton = this.shadowRoot.querySelector(".dismiss-button");
|
||||
this._message = this.shadowRoot.querySelector(".message");
|
||||
this._overlay = this.shadowRoot.querySelector(".overlay");
|
||||
this._title = this.shadowRoot.querySelector(".title");
|
||||
|
||||
this._buttons.classList.toggle("macosx", navigator.platform == "MacIntel");
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "keydown":
|
||||
if (event.repeat) {
|
||||
// Prevent repeat keypresses from accidentally confirming the
|
||||
// dialog since the confirmation button is focused by default.
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (event.key === "Escape" && !event.defaultPrevented) {
|
||||
this.onCancel();
|
||||
}
|
||||
@ -35,7 +44,7 @@ export default class ConfirmDeleteDialog extends HTMLElement {
|
||||
case "click":
|
||||
if (
|
||||
event.target.classList.contains("cancel-button") ||
|
||||
event.target.classList.contains("dismiss-button") ||
|
||||
event.currentTarget.classList.contains("dismiss-button") ||
|
||||
event.target.classList.contains("overlay")
|
||||
) {
|
||||
this.onCancel();
|
||||
@ -45,7 +54,28 @@ export default class ConfirmDeleteDialog extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
setKeyboardAccessForElementsExternalToDialog(enableTabbingOutsideDialog) {
|
||||
const pageElements = document.querySelectorAll(
|
||||
"login-item, login-list, menu-button, login-filter, fxaccounts-button, [tabindex]"
|
||||
);
|
||||
|
||||
pageElements.forEach(el => {
|
||||
if (!enableTabbingOutsideDialog) {
|
||||
if (el.tabIndex > -1) {
|
||||
el.dataset.oldTabIndex = el.tabIndex;
|
||||
}
|
||||
el.tabIndex = "-1";
|
||||
} else if (el.dataset.oldTabIndex) {
|
||||
el.tabIndex = el.dataset.oldTabIndex;
|
||||
delete el.dataset.oldTabIndex;
|
||||
} else {
|
||||
el.removeAttribute("tabindex");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setKeyboardAccessForElementsExternalToDialog(true);
|
||||
this._cancelButton.removeEventListener("click", this);
|
||||
this._confirmButton.removeEventListener("click", this);
|
||||
this._dismissButton.removeEventListener("click", this);
|
||||
@ -55,18 +85,24 @@ export default class ConfirmDeleteDialog extends HTMLElement {
|
||||
this.hidden = true;
|
||||
}
|
||||
|
||||
show() {
|
||||
show({ title, message, confirmButtonLabel }) {
|
||||
this.setKeyboardAccessForElementsExternalToDialog(false);
|
||||
this.hidden = false;
|
||||
|
||||
document.l10n.setAttributes(this._title, title);
|
||||
document.l10n.setAttributes(this._message, message);
|
||||
document.l10n.setAttributes(this._confirmButton, confirmButtonLabel);
|
||||
|
||||
this._cancelButton.addEventListener("click", this);
|
||||
this._confirmButton.addEventListener("click", this);
|
||||
this._dismissButton.addEventListener("click", this);
|
||||
this._overlay.addEventListener("click", this);
|
||||
window.addEventListener("keydown", this);
|
||||
|
||||
// For accessibility, focus the least destructive action button when the
|
||||
// dialog loads.
|
||||
this._cancelButton.focus();
|
||||
// For speed-of-use, focus the confirm button when the
|
||||
// dialog loads. Showing the dialog itself provides enough
|
||||
// of a buffer for accidental deletions.
|
||||
this._confirmButton.focus();
|
||||
|
||||
this._promise = new Promise((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
@ -86,4 +122,4 @@ export default class ConfirmDeleteDialog extends HTMLElement {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
customElements.define("confirm-delete-dialog", ConfirmDeleteDialog);
|
||||
customElements.define("confirmation-dialog", ConfirmationDialog);
|
@ -2,25 +2,31 @@
|
||||
* 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/. */
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.filter[type="text"] {
|
||||
-moz-context-properties: fill, fill-opacity;
|
||||
fill: currentColor;
|
||||
fill-opacity: 0.4;
|
||||
|
||||
background-image: url("chrome://global/skin/icons/search.svg");
|
||||
background-position: 4px 50%;
|
||||
background-position: 8px center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px;
|
||||
flex: auto;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 4px 0;
|
||||
padding-inline-start: 28px;
|
||||
padding-block: 6px;
|
||||
/* We use separate RTL rules over logical properties since we want the visual direction
|
||||
to be independent from the user input direction */
|
||||
padding-left: 32px;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.filter:focus-within {
|
||||
fill-opacity: 0.9;
|
||||
:host(:dir(rtl)) .filter {
|
||||
background-position-x: right 8px;
|
||||
padding-right: 32px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.filter:focus {
|
||||
fill-opacity: 0.8;
|
||||
}
|
||||
|
@ -3,25 +3,30 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:host {
|
||||
padding: 18px;
|
||||
padding: 40px;
|
||||
|
||||
--reveal-checkbox-opacity: .8;
|
||||
--reveal-checkbox-opacity-hover: .6;
|
||||
--reveal-checkbox-opacity-active: 1;
|
||||
--success-color: #00c100;
|
||||
--edit-delete-button-color: #4a4a4f;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:host {
|
||||
--reveal-checkbox-opacity: .8;
|
||||
--reveal-checkbox-opacity-hover: 1;
|
||||
--reveal-checkbox-opacity-active: .6;
|
||||
--success-color: #86DE74;
|
||||
--edit-delete-button-color: #cfcfd1;
|
||||
}
|
||||
}
|
||||
|
||||
:host([data-editing]) .edit-button,
|
||||
:host([data-editing]) .copy-button,
|
||||
:host([data-editing]) .open-site-button,
|
||||
:host([data-is-new-login]) .delete-button,
|
||||
:host([data-is-new-login]) .origin-saved-value,
|
||||
:host([data-is-new-login]) copy-to-clipboard-button,
|
||||
:host([data-is-new-login]) .open-site-button,
|
||||
:host([data-is-new-login]) .meta-info,
|
||||
:host([data-is-new-login]) .login-item-title,
|
||||
:host(:not([data-is-new-login])) .new-login-title,
|
||||
@ -30,28 +35,37 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host(:not([data-editing])) input[type="password"],
|
||||
:host(:not([data-editing])) input[type="text"],
|
||||
:host(:not([data-editing])) input[type="url"] {
|
||||
input[type="password"][readOnly],
|
||||
input[type="text"][readOnly],
|
||||
input[type="url"][readOnly] {
|
||||
all: unset;
|
||||
font-size: 1.1em;
|
||||
display: inline-block;
|
||||
width: -moz-available;
|
||||
background-color: transparent !important; /* override common.inc.css */
|
||||
}
|
||||
|
||||
.detail-cell input:not([type="checkbox"]),
|
||||
.save-changes-button {
|
||||
margin-inline-start: 0; /* align all elements on the start side */
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--in-content-box-border-color);
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
flex: auto;
|
||||
margin-block: 0;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.delete-button,
|
||||
.edit-button {
|
||||
color: var(--edit-delete-button-color) !important;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 8px;
|
||||
-moz-context-properties: fill;
|
||||
@ -61,7 +75,7 @@
|
||||
|
||||
.delete-button:dir(rtl),
|
||||
.edit-button:dir(rtl) {
|
||||
background-position: right 8px center;
|
||||
background-position-x: right 8px;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
@ -74,36 +88,33 @@
|
||||
padding-inline-start: 32px; /* 8px on each side, and 16px for icon width */
|
||||
}
|
||||
|
||||
:host(:not([data-editing])) input[type="url"] {
|
||||
input[type="url"][readOnly] {
|
||||
color: var(--in-content-link-color) !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:host(:not([data-editing])) input[type="url"]:hover {
|
||||
input[type="url"][readOnly]:hover {
|
||||
color: var(--in-content-link-color-hover) !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
:host(:not([data-editing])) input[type="url"]:hover:active {
|
||||
input[type="url"][readOnly]:hover:active {
|
||||
color: var(--in-content-link-color-active) !important;
|
||||
}
|
||||
|
||||
.detail-row,
|
||||
.reveal-password-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.detail-cell {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.detail-row > button {
|
||||
align-self: end;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
@ -113,6 +124,10 @@
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
:host([data-editing]) .detail-cell input:not([type="checkbox"]) {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.copy-button:not([data-copied]) .copied-button-text,
|
||||
.copy-button[data-copied] .copy-button-text {
|
||||
display: none;
|
||||
@ -129,8 +144,17 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.copied-button-text {
|
||||
background-image: url(chrome://global/skin/icons/check.svg);
|
||||
background-repeat: no-repeat;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
padding-inline-start: 22px;
|
||||
}
|
||||
|
||||
.meta-info {
|
||||
font-size: smaller;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
}
|
||||
|
||||
.meta-info:first-of-type {
|
||||
@ -178,11 +202,3 @@
|
||||
-moz-outline-radius: 3px;
|
||||
box-shadow: 0 0 0 4px var(--in-content-border-active-shadow);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:host {
|
||||
--reveal-checkbox-opacity: .8;
|
||||
--reveal-checkbox-opacity-hover: 1;
|
||||
--reveal-checkbox-opacity-active: .6;
|
||||
}
|
||||
}
|
||||
|
@ -65,16 +65,16 @@ export default class LoginItem extends HTMLElement {
|
||||
this._copyUsernameButton.addEventListener("click", this);
|
||||
this._deleteButton.addEventListener("click", this);
|
||||
this._editButton.addEventListener("click", this);
|
||||
this._form.addEventListener("submit", this);
|
||||
this._openSiteButton.addEventListener("click", this);
|
||||
this._originInput.addEventListener("click", this);
|
||||
this._revealCheckbox.addEventListener("click", this);
|
||||
this._saveChangesButton.addEventListener("click", this);
|
||||
window.addEventListener("AboutLoginsCreateLogin", this);
|
||||
window.addEventListener("AboutLoginsInitialLoginSelected", this);
|
||||
window.addEventListener("AboutLoginsLoginSelected", this);
|
||||
}
|
||||
|
||||
render() {
|
||||
async render() {
|
||||
document.l10n.setAttributes(this._timeCreated, "login-item-time-created", {
|
||||
timeCreated: this._login.timeCreated || "",
|
||||
});
|
||||
@ -89,10 +89,16 @@ export default class LoginItem extends HTMLElement {
|
||||
this._originInput.defaultValue = this._login.origin || "";
|
||||
this._usernameInput.defaultValue = this._login.username || "";
|
||||
this._passwordInput.defaultValue = this._login.password || "";
|
||||
this._updatePasswordRevealState();
|
||||
document.l10n.setAttributes(
|
||||
this._saveChangesButton,
|
||||
this.dataset.isNewLogin
|
||||
? "login-item-save-new-button"
|
||||
: "login-item-save-changes-button"
|
||||
);
|
||||
await this._updatePasswordRevealState();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
async handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "AboutLoginsCreateLogin": {
|
||||
this.setLogin({});
|
||||
@ -103,7 +109,23 @@ export default class LoginItem extends HTMLElement {
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsLoginSelected": {
|
||||
this.setLogin(event.detail);
|
||||
let login = event.detail;
|
||||
if (this.hasPendingChanges()) {
|
||||
event.preventDefault();
|
||||
this.showConfirmationDialog("discard-changes", () => {
|
||||
// Clear any pending changes
|
||||
this.setLogin(login);
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsLoginSelected", {
|
||||
detail: login,
|
||||
cancelable: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.setLogin(login);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "blur": {
|
||||
@ -120,18 +142,22 @@ export default class LoginItem extends HTMLElement {
|
||||
case "click": {
|
||||
let classList = event.currentTarget.classList;
|
||||
if (classList.contains("reveal-password-checkbox")) {
|
||||
this._updatePasswordRevealState();
|
||||
await this._updatePasswordRevealState();
|
||||
|
||||
let method = this._revealCheckbox.checked ? "show" : "hide";
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent form submit behavior on the following buttons.
|
||||
event.preventDefault();
|
||||
if (classList.contains("cancel-button")) {
|
||||
let wasExistingLogin = !!this._login.guid;
|
||||
if (wasExistingLogin) {
|
||||
this.setLogin(this._login);
|
||||
if (this.hasPendingChanges()) {
|
||||
this.showConfirmationDialog("discard-changes", () => {
|
||||
this.setLogin(this._login);
|
||||
});
|
||||
} else {
|
||||
this.setLogin(this._login);
|
||||
}
|
||||
} else {
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsClearSelection"));
|
||||
}
|
||||
@ -142,18 +168,30 @@ export default class LoginItem extends HTMLElement {
|
||||
classList.contains("copy-username-button")
|
||||
) {
|
||||
let copyButton = event.currentTarget;
|
||||
if (copyButton.dataset.copyLoginProperty == "password") {
|
||||
let masterPasswordAuth = await new Promise(resolve => {
|
||||
window.AboutLoginsUtils.promptForMasterPassword(resolve);
|
||||
});
|
||||
if (!masterPasswordAuth) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
copyButton.disabled = true;
|
||||
let propertyToCopy = copyButton.dataset.copyLoginProperty;
|
||||
navigator.clipboard.writeText(this._login[propertyToCopy]).then(
|
||||
() => {
|
||||
copyButton.dataset.copied = true;
|
||||
setTimeout(() => {
|
||||
copyButton.disabled = false;
|
||||
delete copyButton.dataset.copied;
|
||||
}, LoginItem.COPY_BUTTON_RESET_TIMEOUT);
|
||||
},
|
||||
() => (copyButton.disabled = false)
|
||||
copyButton.dataset.copied = true;
|
||||
let propertyToCopy = this._login[
|
||||
copyButton.dataset.copyLoginProperty
|
||||
];
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsCopyLoginDetail", {
|
||||
bubbles: true,
|
||||
detail: propertyToCopy,
|
||||
})
|
||||
);
|
||||
setTimeout(() => {
|
||||
copyButton.disabled = false;
|
||||
delete copyButton.dataset.copied;
|
||||
}, LoginItem.COPY_BUTTON_RESET_TIMEOUT);
|
||||
return;
|
||||
}
|
||||
if (classList.contains("delete-button")) {
|
||||
@ -166,7 +204,7 @@ export default class LoginItem extends HTMLElement {
|
||||
}
|
||||
if (
|
||||
classList.contains("open-site-button") ||
|
||||
(classList.contains("origin-input") && !this.dataset.editing)
|
||||
(classList.contains("origin-input") && !this.readOnly)
|
||||
) {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsOpenSite", {
|
||||
@ -174,52 +212,91 @@ export default class LoginItem extends HTMLElement {
|
||||
detail: this._login,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (classList.contains("save-changes-button")) {
|
||||
if (!this._isFormValid({ reportErrors: true })) {
|
||||
return;
|
||||
}
|
||||
let loginUpdates = this._loginFromForm();
|
||||
if (this._login.guid) {
|
||||
loginUpdates.guid = this._login.guid;
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsUpdateLogin", {
|
||||
bubbles: true,
|
||||
detail: loginUpdates,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsCreateLogin", {
|
||||
bubbles: true,
|
||||
detail: loginUpdates,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "submit": {
|
||||
// Prevent page navigation form submit behavior.
|
||||
event.preventDefault();
|
||||
if (!this._isFormValid({ reportErrors: true })) {
|
||||
return;
|
||||
}
|
||||
let loginUpdates = this._loginFromForm();
|
||||
if (this._login.guid) {
|
||||
loginUpdates.guid = this._login.guid;
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsUpdateLogin", {
|
||||
bubbles: true,
|
||||
detail: loginUpdates,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsCreateLogin", {
|
||||
bubbles: true,
|
||||
detail: loginUpdates,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog.
|
||||
* @param {string} type The type of confirmation dialog to display.
|
||||
* @param {boolean} onConfirm Optional, the function to execute when the confirm button is clicked.
|
||||
*/
|
||||
showConfirmationDialog(type, onConfirm = () => {}) {
|
||||
const dialog = document.querySelector("confirmation-dialog");
|
||||
let options;
|
||||
switch (type) {
|
||||
case "delete": {
|
||||
options = {
|
||||
title: "confirm-delete-dialog-title",
|
||||
message: "confirm-delete-dialog-message",
|
||||
confirmButtonLabel: "confirm-delete-dialog-confirm-button",
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "discard-changes": {
|
||||
options = {
|
||||
title: "confirm-discard-changes-dialog-title",
|
||||
message: "confirm-discard-changes-dialog-message",
|
||||
confirmButtonLabel: "confirm-discard-changes-dialog-confirm-button",
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
let dialogPromise = dialog.show(options);
|
||||
dialogPromise.then(onConfirm, () => {});
|
||||
return dialogPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the confirm delete dialog, completing the deletion if the user
|
||||
* agrees.
|
||||
*/
|
||||
confirmDelete() {
|
||||
const dialog = document.querySelector("confirm-delete-dialog");
|
||||
dialog.show().then(
|
||||
() => {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsDeleteLogin", {
|
||||
bubbles: true,
|
||||
detail: this._login,
|
||||
})
|
||||
);
|
||||
},
|
||||
() => {}
|
||||
this.showConfirmationDialog("delete", () => {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsDeleteLogin", {
|
||||
bubbles: true,
|
||||
detail: this._login,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
hasPendingChanges() {
|
||||
let { origin = "", username = "", password = "" } = this._login || {};
|
||||
|
||||
let valuesChanged = !window.AboutLoginsUtils.doLoginsMatch(
|
||||
{ origin, username, password },
|
||||
this._loginFromForm()
|
||||
);
|
||||
|
||||
return this.dataset.editing && valuesChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -361,7 +438,7 @@ export default class LoginItem extends HTMLElement {
|
||||
this._deleteButton.disabled = this.dataset.isNewLogin;
|
||||
this._editButton.disabled = shouldEdit;
|
||||
let inputTabIndex = !shouldEdit ? -1 : 0;
|
||||
this._originInput.readOnly = !shouldEdit;
|
||||
this._originInput.readOnly = !this.dataset.isNewLogin;
|
||||
this._originInput.tabIndex = inputTabIndex;
|
||||
this._usernameInput.readOnly = !shouldEdit;
|
||||
this._usernameInput.tabIndex = inputTabIndex;
|
||||
@ -375,7 +452,17 @@ export default class LoginItem extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
_updatePasswordRevealState() {
|
||||
async _updatePasswordRevealState() {
|
||||
if (this._revealCheckbox.checked) {
|
||||
let masterPasswordAuth = await new Promise(resolve => {
|
||||
window.AboutLoginsUtils.promptForMasterPassword(resolve);
|
||||
});
|
||||
if (!masterPasswordAuth) {
|
||||
this._revealCheckbox.checked = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let titleId = this._revealCheckbox.checked
|
||||
? "login-item-password-reveal-checkbox-hide"
|
||||
: "login-item-password-reveal-checkbox-show";
|
||||
|
@ -10,16 +10,18 @@
|
||||
*/
|
||||
export default class LoginListItemFactory {
|
||||
static create(login) {
|
||||
let loginListItemTemplate = document.querySelector(
|
||||
"#login-list-item-template"
|
||||
);
|
||||
let loginListItem = loginListItemTemplate.content.cloneNode(true);
|
||||
let listItem = loginListItem.querySelector("li");
|
||||
let title = loginListItem.querySelector(".title");
|
||||
let username = loginListItem.querySelector(".username");
|
||||
let template = document.querySelector("#login-list-item-template");
|
||||
let fragment = template.content.cloneNode(true);
|
||||
let listItem = fragment.firstElementChild;
|
||||
|
||||
listItem.setAttribute("role", "option");
|
||||
LoginListItemFactory.update(listItem, login);
|
||||
|
||||
return listItem;
|
||||
}
|
||||
|
||||
static update(listItem, login) {
|
||||
let title = listItem.querySelector(".title");
|
||||
let username = listItem.querySelector(".username");
|
||||
if (!login.guid) {
|
||||
listItem.id = "new-login-list-item";
|
||||
document.l10n.setAttributes(title, "login-list-item-title-new-login");
|
||||
@ -27,24 +29,30 @@ export default class LoginListItemFactory {
|
||||
username,
|
||||
"login-list-item-subtitle-new-login"
|
||||
);
|
||||
return listItem;
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepend the ID with a string since IDs must not begin with a number.
|
||||
listItem.id = "lli-" + login.guid;
|
||||
listItem.dataset.guid = login.guid;
|
||||
if (!listItem.id) {
|
||||
listItem.id = "lli-" + login.guid;
|
||||
listItem.dataset.guid = login.guid;
|
||||
}
|
||||
listItem._login = login;
|
||||
title.textContent = login.title;
|
||||
if (login.username.trim()) {
|
||||
username.removeAttribute("data-l10n-id");
|
||||
username.textContent = login.username.trim();
|
||||
if (title.textContent != login.title) {
|
||||
title.textContent = login.title;
|
||||
}
|
||||
|
||||
let trimmedUsernameValue = login.username.trim();
|
||||
if (trimmedUsernameValue) {
|
||||
if (username.textContent != trimmedUsernameValue) {
|
||||
username.removeAttribute("data-l10n-id");
|
||||
username.textContent = trimmedUsernameValue;
|
||||
}
|
||||
} else {
|
||||
document.l10n.setAttributes(
|
||||
username,
|
||||
"login-list-item-subtitle-missing-username"
|
||||
);
|
||||
}
|
||||
|
||||
return listItem;
|
||||
}
|
||||
}
|
||||
|
@ -13,13 +13,26 @@
|
||||
.meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 18px;
|
||||
padding: 5px 18px;
|
||||
border-bottom: 1px solid var(--in-content-box-border-color);
|
||||
background-color: var(--in-content-box-info-background);
|
||||
background-color: var(--in-content-box-background);
|
||||
color: var(--in-content-deemphasized-text);
|
||||
}
|
||||
|
||||
#login-sort {
|
||||
min-height: initial;
|
||||
line-height: 1.1;
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
color: var(--in-content-text-color) !important;
|
||||
}
|
||||
|
||||
#login-sort > option {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.count {
|
||||
flex: auto;
|
||||
flex-grow: 1;
|
||||
text-align: end;
|
||||
font-size: smaller;
|
||||
margin-inline-start: 18px;
|
||||
@ -35,7 +48,7 @@ ol {
|
||||
}
|
||||
|
||||
.create-login-button {
|
||||
margin: 18px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.login-list-item {
|
||||
@ -65,14 +78,23 @@ ol {
|
||||
background-color: var(--in-content-box-background-hover);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
.login-list-item.selected .title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.labels {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.title,
|
||||
.username {
|
||||
display: block;
|
||||
max-width: 50ch;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 0.85em;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
}
|
||||
|
@ -14,7 +14,10 @@ const sortFnOptions = {
|
||||
export default class LoginList extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this._logins = [];
|
||||
// An array of login GUIDs, stored in sorted order.
|
||||
this._loginGuidsSortedOrder = [];
|
||||
// A map of login GUID -> {login, listItem}.
|
||||
this._logins = {};
|
||||
this._filter = "";
|
||||
this._selectedGuid = null;
|
||||
this._blankLoginListItem = LoginListItemFactory.create({});
|
||||
@ -32,6 +35,7 @@ export default class LoginList extends HTMLElement {
|
||||
this._count = shadowRoot.querySelector(".count");
|
||||
this._createLoginButton = shadowRoot.querySelector(".create-login-button");
|
||||
this._list = shadowRoot.querySelector("ol");
|
||||
this._list.appendChild(this._blankLoginListItem);
|
||||
this._sortSelect = shadowRoot.querySelector("#login-sort");
|
||||
|
||||
this.render();
|
||||
@ -48,63 +52,45 @@ export default class LoginList extends HTMLElement {
|
||||
this._createLoginButton.addEventListener("click", this);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} options optional
|
||||
* createLogin: When set to true will show and select
|
||||
* a blank login-list-item.
|
||||
*/
|
||||
async render(options = {}) {
|
||||
this._list.textContent = "";
|
||||
|
||||
if (options.createLogin) {
|
||||
this._blankLoginListItem.classList.add("selected");
|
||||
this._blankLoginListItem.setAttribute("aria-selected", "true");
|
||||
this._list.setAttribute(
|
||||
"aria-activedescendant",
|
||||
this._blankLoginListItem.id
|
||||
);
|
||||
this._list.appendChild(this._blankLoginListItem);
|
||||
} else {
|
||||
this._blankLoginListItem.remove();
|
||||
}
|
||||
|
||||
if (!this._logins.length) {
|
||||
document.l10n.setAttributes(this._count, "login-list-count", {
|
||||
count: 0,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let visibleLogins = this._applyFilter();
|
||||
document.l10n.setAttributes(this._count, "login-list-count", {
|
||||
count: visibleLogins.length,
|
||||
});
|
||||
async render() {
|
||||
let visibleLoginGuids = this._applyFilter();
|
||||
this._updateVisibleLoginCount(visibleLoginGuids.size);
|
||||
|
||||
// Add all of the logins that are not in the DOM yet.
|
||||
let fragment = document.createDocumentFragment();
|
||||
let chunkSize = 5;
|
||||
for (let i = 0; i < this._logins.length; i++) {
|
||||
let login = this._logins[i];
|
||||
for (let guid of this._loginGuidsSortedOrder) {
|
||||
if (this._logins[guid].listItem) {
|
||||
continue;
|
||||
}
|
||||
let login = this._logins[guid].login;
|
||||
let listItem = LoginListItemFactory.create(login);
|
||||
if (login.guid == this._selectedGuid) {
|
||||
this._logins[login.guid] = Object.assign(this._logins[login.guid], {
|
||||
listItem,
|
||||
});
|
||||
fragment.appendChild(listItem);
|
||||
}
|
||||
this._list.appendChild(fragment);
|
||||
|
||||
// Show, hide, and update state of the list items per the applied search filter.
|
||||
for (let guid of this._loginGuidsSortedOrder) {
|
||||
let { listItem } = this._logins[guid];
|
||||
if (guid == this._selectedGuid) {
|
||||
this._setListItemAsSelected(listItem);
|
||||
}
|
||||
|
||||
if (!visibleLogins.includes(login.guid)) {
|
||||
listItem.hidden = true;
|
||||
}
|
||||
|
||||
fragment.appendChild(listItem);
|
||||
|
||||
// Display a first chunk of logins ASAP to improve perceived performance,
|
||||
// then append progressively larger chunks until complete.
|
||||
if (i == chunkSize) {
|
||||
this._list.appendChild(fragment);
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
chunkSize *= chunkSize;
|
||||
}
|
||||
listItem.hidden = !visibleLoginGuids.has(listItem.dataset.guid);
|
||||
}
|
||||
this._blankLoginListItem.hidden = this._selectedGuid != null;
|
||||
|
||||
// Re-arrange the login-list-items according to their sort
|
||||
for (let i = this._loginGuidsSortedOrder.length - 1; i >= 0; i--) {
|
||||
let guid = this._loginGuidsSortedOrder[i];
|
||||
let { listItem } = this._logins[guid];
|
||||
this._list.insertBefore(
|
||||
listItem,
|
||||
this._blankLoginListItem.nextElementSibling
|
||||
);
|
||||
}
|
||||
this._list.appendChild(fragment);
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
@ -115,8 +101,8 @@ export default class LoginList extends HTMLElement {
|
||||
return;
|
||||
}
|
||||
|
||||
let loginListItem = event.originalTarget.closest(".login-list-item");
|
||||
if (!loginListItem || !loginListItem.dataset.guid) {
|
||||
let listItem = event.originalTarget.closest(".login-list-item");
|
||||
if (!listItem || !listItem.dataset.guid) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -124,31 +110,32 @@ export default class LoginList extends HTMLElement {
|
||||
new CustomEvent("AboutLoginsLoginSelected", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: loginListItem._login,
|
||||
cancelable: true, // allow calling preventDefault() on event
|
||||
detail: listItem._login,
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "change": {
|
||||
const sort = this._sortSelect.value;
|
||||
this._logins = this._logins.sort((a, b) => sortFnOptions[sort](a, b));
|
||||
this._applySort();
|
||||
this.render();
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsClearSelection": {
|
||||
if (!this._logins.length) {
|
||||
if (!this._loginGuidsSortedOrder.length) {
|
||||
return;
|
||||
}
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsLoginSelected", {
|
||||
detail: this._logins[0],
|
||||
cancelable: true,
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsCreateLogin": {
|
||||
this._selectedGuid = null;
|
||||
this.render({ createLogin: true });
|
||||
this._setListItemAsSelected(this._blankLoginListItem);
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsFilterLogins": {
|
||||
@ -157,7 +144,7 @@ export default class LoginList extends HTMLElement {
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsLoginSelected": {
|
||||
if (this._selectedGuid == event.detail.guid) {
|
||||
if (event.defaultPrevented || this._selectedGuid == event.detail.guid) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -182,16 +169,18 @@ export default class LoginList extends HTMLElement {
|
||||
* @param {login[]} logins An array of logins used for displaying in the list.
|
||||
*/
|
||||
setLogins(logins) {
|
||||
this._logins = logins;
|
||||
const sort = this._sortSelect.value;
|
||||
this._logins = this._logins.sort((a, b) => sortFnOptions[sort](a, b));
|
||||
|
||||
this._loginGuidsSortedOrder = [];
|
||||
this._logins = logins.reduce((map, login) => {
|
||||
this._loginGuidsSortedOrder.push(login.guid);
|
||||
map[login.guid] = { login };
|
||||
return map;
|
||||
}, {});
|
||||
this._applySort();
|
||||
this._list.textContent = "";
|
||||
this._list.appendChild(this._blankLoginListItem);
|
||||
this.render();
|
||||
|
||||
if (
|
||||
!this._selectedGuid ||
|
||||
!this._logins.findIndex(login => login.guid == this._selectedGuid) != -1
|
||||
) {
|
||||
if (!this._selectedGuid || !this._logins[this._selectedGuid]) {
|
||||
// Select the first visible login after any possible filter is applied.
|
||||
let firstVisibleListItem = this._list.querySelector(
|
||||
".login-list-item[data-guid]:not([hidden])"
|
||||
@ -212,56 +201,106 @@ export default class LoginList extends HTMLElement {
|
||||
* @param {login} login A login that was added to storage.
|
||||
*/
|
||||
loginAdded(login) {
|
||||
this._logins.push(login);
|
||||
this._logins[login.guid] = { login };
|
||||
this._loginGuidsSortedOrder.push(login.guid);
|
||||
this._applySort();
|
||||
|
||||
// Add the list item and update any other related state that may pertain
|
||||
// to the list item such as breach alerts.
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {login} login A login that was modified in storage. The related login-list-item
|
||||
* will get updated.
|
||||
* @param {login} login A login that was modified in storage. The related
|
||||
* login-list-item will get updated.
|
||||
*/
|
||||
loginModified(login) {
|
||||
for (let i = 0; i < this._logins.length; i++) {
|
||||
if (this._logins[i].guid == login.guid) {
|
||||
this._logins[i] = login;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._logins[login.guid] = Object.assign(this._logins[login.guid], {
|
||||
login,
|
||||
});
|
||||
this._applySort();
|
||||
let { listItem } = this._logins[login.guid];
|
||||
LoginListItemFactory.update(listItem, login);
|
||||
|
||||
// Update any other related state that may pertain to the list item
|
||||
// such as breach alerts that may or may not now apply.
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {login} login A login that was removed from storage. The related login-list-item
|
||||
* will get removed. The login object is a plain JS object
|
||||
* representation of nsILoginInfo/nsILoginMetaInfo.
|
||||
* @param {login} login A login that was removed from storage. The related
|
||||
* login-list-item will get removed. The login object
|
||||
* is a plain JS object representation of
|
||||
* nsILoginInfo/nsILoginMetaInfo.
|
||||
*/
|
||||
loginRemoved(login) {
|
||||
this._logins = this._logins.filter(l => l.guid != login.guid);
|
||||
this.render();
|
||||
this._logins[login.guid].listItem.remove();
|
||||
|
||||
// Update the selected list item to the previous item in the list
|
||||
// if one exists, otherwise the next item. If no logins remain
|
||||
// the login-intro text will be shown instead of the login-list.
|
||||
if (this._selectedGuid == login.guid) {
|
||||
let index = this._loginGuidsSortedOrder.indexOf(login.guid);
|
||||
if (this._loginGuidsSortedOrder.length > 1) {
|
||||
let newlySelectedIndex = index > 0 ? index - 1 : index + 1;
|
||||
let newlySelectedListItem = this._logins[
|
||||
this._loginGuidsSortedOrder[newlySelectedIndex]
|
||||
].listItem;
|
||||
this._setListItemAsSelected(newlySelectedListItem);
|
||||
}
|
||||
}
|
||||
|
||||
delete this._logins[login.guid];
|
||||
this._loginGuidsSortedOrder = this._loginGuidsSortedOrder.filter(guid => {
|
||||
return guid != login.guid;
|
||||
});
|
||||
|
||||
let visibleLoginGuids = this._applyFilter();
|
||||
this._updateVisibleLoginCount(visibleLoginGuids.size);
|
||||
|
||||
// Since the login has been removed, we don't need to call render
|
||||
// as nothing related to the login needs updating.
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the displayed logins in the list to only those matching the
|
||||
* cached filter value.
|
||||
* @returns {Set} Set of login guids that match the filter.
|
||||
*/
|
||||
_applyFilter() {
|
||||
let matchingLoginGuids;
|
||||
if (this._filter) {
|
||||
matchingLoginGuids = this._logins
|
||||
.filter(login => {
|
||||
matchingLoginGuids = new Set(
|
||||
this._loginGuidsSortedOrder.filter(guid => {
|
||||
let { login } = this._logins[guid];
|
||||
return (
|
||||
login.origin.toLocaleLowerCase().includes(this._filter) ||
|
||||
login.username.toLocaleLowerCase().includes(this._filter)
|
||||
);
|
||||
})
|
||||
.map(login => login.guid);
|
||||
);
|
||||
} else {
|
||||
matchingLoginGuids = this._logins.map(login => login.guid);
|
||||
matchingLoginGuids = new Set([...this._loginGuidsSortedOrder]);
|
||||
}
|
||||
|
||||
return matchingLoginGuids;
|
||||
}
|
||||
|
||||
_applySort() {
|
||||
const sort = this._sortSelect.value;
|
||||
this._loginGuidsSortedOrder = this._loginGuidsSortedOrder.sort((a, b) => {
|
||||
let loginA = this._logins[a].login;
|
||||
let loginB = this._logins[b].login;
|
||||
return sortFnOptions[sort](loginA, loginB);
|
||||
});
|
||||
}
|
||||
|
||||
_updateVisibleLoginCount(count) {
|
||||
if (count != document.l10n.getAttributes(this._count).args.count) {
|
||||
document.l10n.setAttributes(this._count, "login-list-count", {
|
||||
count,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_handleKeyboardNav(event) {
|
||||
if (
|
||||
this._createLoginButton == this.shadowRoot.activeElement &&
|
||||
@ -349,9 +388,7 @@ export default class LoginList extends HTMLElement {
|
||||
oldSelectedItem.classList.remove("selected");
|
||||
oldSelectedItem.removeAttribute("aria-selected");
|
||||
}
|
||||
if (listItem.dataset.guid) {
|
||||
this._blankLoginListItem.remove();
|
||||
}
|
||||
this._blankLoginListItem.hidden = !!listItem.dataset.guid;
|
||||
listItem.classList.add("selected");
|
||||
listItem.setAttribute("aria-selected", "true");
|
||||
this._list.setAttribute("aria-activedescendant", listItem.id);
|
||||
|
@ -14,49 +14,50 @@
|
||||
fill: currentColor;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 0;
|
||||
inset-inline-end: 0;
|
||||
margin: 0;
|
||||
padding: 5px 0;
|
||||
background-color: var(--in-content-box-background);
|
||||
border: 1px solid var(--in-content-box-border-color);
|
||||
border-radius: 2px;
|
||||
box-shadow: var(--shadow-10);
|
||||
border-radius: 5px;
|
||||
box-shadow: var(--shadow-30);
|
||||
min-width: max-content;
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.menu:dir(rtl) {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.menuitem-button {
|
||||
padding: 4px 8px;
|
||||
padding: 2px 8px;
|
||||
/* 32px = 8px (padding) + 16px (icon) + 8px (padding) */
|
||||
padding-inline-start: 32px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left 8px center;
|
||||
background-size: 16px;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
/* Override common.inc.css box-shadow on these buttons */
|
||||
box-shadow: none !important;
|
||||
text-align: start;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
|
||||
/* Override common.inc.css properties */
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
text-align: start;
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
.menuitem-button:dir(rtl) {
|
||||
background-position: right 8px center;
|
||||
background-position-x: right 8px;
|
||||
}
|
||||
|
||||
.menuitem-separator {
|
||||
border-top-width: 2px;
|
||||
margin-block: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menuitem-import {
|
||||
@ -66,3 +67,4 @@
|
||||
.menuitem-preferences {
|
||||
background-image: url("chrome://browser/skin/settings.svg");
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/aboutlogins/components/confirm-delete-dialog.css (content/components/confirm-delete-dialog.css)
|
||||
content/browser/aboutlogins/components/confirm-delete-dialog.js (content/components/confirm-delete-dialog.js)
|
||||
content/browser/aboutlogins/components/confirmation-dialog.css (content/components/confirmation-dialog.css)
|
||||
content/browser/aboutlogins/components/confirmation-dialog.js (content/components/confirmation-dialog.js)
|
||||
content/browser/aboutlogins/components/login-filter.css (content/components/login-filter.css)
|
||||
content/browser/aboutlogins/components/login-filter.js (content/components/login-filter.js)
|
||||
content/browser/aboutlogins/components/login-item.css (content/components/login-item.css)
|
||||
|
@ -5,7 +5,7 @@
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'Password Manager')
|
||||
BUG_COMPONENT = ('Firefox', 'about:logins')
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'AboutLoginsParent.jsm',
|
||||
|
@ -11,6 +11,7 @@ support-files =
|
||||
[browser_deleteLogin.js]
|
||||
[browser_loginListChanges.js]
|
||||
[browser_masterPassword.js]
|
||||
skip-if = (os == 'linux') # bug 1569789
|
||||
[browser_openFiltered.js]
|
||||
[browser_openImport.js]
|
||||
skip-if = (os != "win") # import is only available on Windows
|
||||
|
@ -15,10 +15,13 @@ add_task(async function test() {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
|
||||
await ContentTask.spawn(browser, null, async () => {
|
||||
let dialog = Cu.waiveXrays(
|
||||
content.document.querySelector("confirm-delete-dialog")
|
||||
);
|
||||
let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
|
||||
|
||||
let showPromise = loginItem.showConfirmationDialog("delete");
|
||||
|
||||
let dialog = Cu.waiveXrays(
|
||||
content.document.querySelector("confirmation-dialog")
|
||||
);
|
||||
let cancelButton = dialog.shadowRoot.querySelector(".cancel-button");
|
||||
let confirmDeleteButton = dialog.shadowRoot.querySelector(
|
||||
".confirm-button"
|
||||
@ -27,14 +30,20 @@ add_task(async function test() {
|
||||
let message = dialog.shadowRoot.querySelector(".message");
|
||||
let title = dialog.shadowRoot.querySelector(".title");
|
||||
|
||||
await content.document.l10n.translateElements([
|
||||
title,
|
||||
message,
|
||||
confirmDeleteButton,
|
||||
]);
|
||||
|
||||
is(
|
||||
title.textContent,
|
||||
"Confirm Deletion",
|
||||
"Delete this login?",
|
||||
"Title contents should match l10n attribute set on outer element"
|
||||
);
|
||||
is(
|
||||
message.textContent,
|
||||
"Are you sure you want to delete this login?",
|
||||
"This action cannot be undone.",
|
||||
"Message contents should match l10n attribute set on outer element"
|
||||
);
|
||||
is(
|
||||
@ -44,11 +53,10 @@ add_task(async function test() {
|
||||
);
|
||||
is(
|
||||
confirmDeleteButton.textContent,
|
||||
"Delete login",
|
||||
"Delete",
|
||||
"Delete button contents should match l10n attribute set on outer element"
|
||||
);
|
||||
|
||||
let showPromise = dialog.show();
|
||||
cancelButton.click();
|
||||
try {
|
||||
await showPromise;
|
||||
@ -68,7 +76,7 @@ add_task(async function test() {
|
||||
);
|
||||
ok(dialog.hidden, "Dialog should be hidden after clicking cancel button");
|
||||
|
||||
showPromise = dialog.show();
|
||||
showPromise = loginItem.showConfirmationDialog("delete");
|
||||
dismissButton.click();
|
||||
try {
|
||||
await showPromise;
|
||||
@ -88,7 +96,7 @@ add_task(async function test() {
|
||||
);
|
||||
ok(dialog.hidden, "Dialog should be hidden after clicking dismiss button");
|
||||
|
||||
showPromise = dialog.show();
|
||||
showPromise = loginItem.showConfirmationDialog("delete");
|
||||
confirmDeleteButton.click();
|
||||
try {
|
||||
await showPromise;
|
||||
|
@ -81,13 +81,13 @@ add_task(async function test_create_login() {
|
||||
content.document.querySelector("login-list")
|
||||
);
|
||||
let loginFound = await ContentTaskUtils.waitForCondition(() => {
|
||||
return loginList._logins.length == aExpectedCount;
|
||||
return loginList._loginGuidsSortedOrder.length == aExpectedCount;
|
||||
}, "Waiting for login to be displayed");
|
||||
ok(loginFound, "Expected number of logins found in login-list");
|
||||
|
||||
let loginListItem = [
|
||||
...loginList.shadowRoot.querySelectorAll(".login-list-item"),
|
||||
].find(l => l._login.origin == aOriginTuple[1]);
|
||||
].find(l => l._login && l._login.origin == aOriginTuple[1]);
|
||||
ok(
|
||||
!!loginListItem,
|
||||
`Stored login should only include the origin of the URL provided during creation (${
|
||||
@ -141,7 +141,9 @@ add_task(async function test_create_login() {
|
||||
let loginList = Cu.waiveXrays(
|
||||
content.document.querySelector("login-list")
|
||||
);
|
||||
let login = loginList._logins.find(l => l.origin == aOriginTuple[1]);
|
||||
let login = Object.values(loginList._logins).find(
|
||||
obj => obj.login.origin == aOriginTuple[1]
|
||||
).login;
|
||||
ok(
|
||||
!!login,
|
||||
"Stored login should only include the origin of the URL provided during creation"
|
||||
|
@ -20,9 +20,9 @@ add_task(async function test_show_logins() {
|
||||
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
|
||||
let loginFound = await ContentTaskUtils.waitForCondition(() => {
|
||||
return (
|
||||
loginList._logins.length == 2 &&
|
||||
loginList._logins[0].guid == logins[0].guid &&
|
||||
loginList._logins[1].guid == logins[1].guid
|
||||
loginList._loginGuidsSortedOrder.length == 2 &&
|
||||
loginList._loginGuidsSortedOrder.includes(logins[0].guid) &&
|
||||
loginList._loginGuidsSortedOrder.includes(logins[1].guid)
|
||||
);
|
||||
}, "Waiting for logins to be displayed");
|
||||
ok(loginFound, "Newly added logins should be added to the page");
|
||||
@ -60,7 +60,7 @@ add_task(async function test_login_item() {
|
||||
deleteButton.click();
|
||||
|
||||
let confirmDeleteDialog = Cu.waiveXrays(
|
||||
content.document.querySelector("confirm-delete-dialog")
|
||||
content.document.querySelector("confirmation-dialog")
|
||||
);
|
||||
let confirmButton = confirmDeleteDialog.shadowRoot.querySelector(
|
||||
".confirm-button"
|
||||
|
@ -25,8 +25,8 @@ add_task(async function test_login_added() {
|
||||
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
|
||||
let loginFound = await ContentTaskUtils.waitForCondition(() => {
|
||||
return (
|
||||
loginList._logins.length == 1 &&
|
||||
loginList._logins[0].guid == addedLogin.guid
|
||||
loginList._loginGuidsSortedOrder.length == 1 &&
|
||||
loginList._loginGuidsSortedOrder[0] == addedLogin.guid
|
||||
);
|
||||
}, "Waiting for login to be added");
|
||||
ok(loginFound, "Newly added logins should be added to the page");
|
||||
@ -47,9 +47,10 @@ add_task(async function test_login_modified() {
|
||||
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
|
||||
let loginFound = await ContentTaskUtils.waitForCondition(() => {
|
||||
return (
|
||||
loginList._logins.length == 1 &&
|
||||
loginList._logins[0].guid == modifiedLogin.guid &&
|
||||
loginList._logins[0].username == modifiedLogin.username
|
||||
loginList._loginGuidsSortedOrder.length == 1 &&
|
||||
loginList._loginGuidsSortedOrder[0] == modifiedLogin.guid &&
|
||||
loginList._logins[loginList._loginGuidsSortedOrder[0]].login.username ==
|
||||
modifiedLogin.username
|
||||
);
|
||||
}, "Waiting for username to get updated");
|
||||
ok(loginFound, "The login should get updated on the page");
|
||||
@ -69,7 +70,7 @@ add_task(async function test_login_removed() {
|
||||
await ContentTask.spawn(browser, login, async removedLogin => {
|
||||
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
|
||||
let loginRemoved = await ContentTaskUtils.waitForCondition(() => {
|
||||
return loginList._logins.length == 0;
|
||||
return loginList._loginGuidsSortedOrder.length == 0;
|
||||
}, "Waiting for login to get removed");
|
||||
ok(loginRemoved, "The login should be removed from the page");
|
||||
});
|
||||
|
@ -33,9 +33,9 @@ function waitForLoginCountToReach(browser, loginCount) {
|
||||
return ContentTask.spawn(browser, loginCount, async expectedLoginCount => {
|
||||
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return loginList._logins.length == expectedLoginCount;
|
||||
return loginList._loginGuidsSortedOrder.length == expectedLoginCount;
|
||||
});
|
||||
return loginList._logins.length;
|
||||
return loginList._loginGuidsSortedOrder.length;
|
||||
});
|
||||
}
|
||||
|
||||
@ -93,4 +93,79 @@ add_task(async function test() {
|
||||
|
||||
logins = await waitForLoginCountToReach(browser, 1);
|
||||
is(logins, 1, "Logins should be displayed when MP is set and authenticated");
|
||||
|
||||
// Show MP dialog when Copy Password button clicked
|
||||
mpDialogShown = waitForMPDialog("cancel");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
let copyButton = loginItem.shadowRoot.querySelector(
|
||||
".copy-password-button"
|
||||
);
|
||||
copyButton.click();
|
||||
});
|
||||
await mpDialogShown;
|
||||
info("Master Password dialog shown and canceled");
|
||||
mpDialogShown = waitForMPDialog("authenticate");
|
||||
info("Clicking copy password button again");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
let copyButton = loginItem.shadowRoot.querySelector(
|
||||
".copy-password-button"
|
||||
);
|
||||
copyButton.click();
|
||||
});
|
||||
await mpDialogShown;
|
||||
info("Master Password dialog shown and authenticated");
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
let copyButton = loginItem.shadowRoot.querySelector(
|
||||
".copy-password-button"
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return copyButton.disabled;
|
||||
}, "Waiting for copy button to be disabled");
|
||||
info("Password was copied to clipboard");
|
||||
});
|
||||
|
||||
// Show MP dialog when Reveal Password checkbox is checked
|
||||
mpDialogShown = waitForMPDialog("cancel");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
let revealCheckbox = loginItem.shadowRoot.querySelector(
|
||||
".reveal-password-checkbox"
|
||||
);
|
||||
revealCheckbox.click();
|
||||
});
|
||||
await mpDialogShown;
|
||||
info("Master Password dialog shown and canceled");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
let revealCheckbox = loginItem.shadowRoot.querySelector(
|
||||
".reveal-password-checkbox"
|
||||
);
|
||||
ok(
|
||||
!revealCheckbox.checked,
|
||||
"reveal checkbox should be unchecked if MP dialog canceled"
|
||||
);
|
||||
});
|
||||
mpDialogShown = waitForMPDialog("authenticate");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
let revealCheckbox = loginItem.shadowRoot.querySelector(
|
||||
".reveal-password-checkbox"
|
||||
);
|
||||
revealCheckbox.click();
|
||||
});
|
||||
await mpDialogShown;
|
||||
info("Master Password dialog shown and authenticated");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
|
||||
let loginItem = content.document.querySelector("login-item");
|
||||
let revealCheckbox = loginItem.shadowRoot.querySelector(
|
||||
".reveal-password-checkbox"
|
||||
);
|
||||
ok(
|
||||
revealCheckbox.checked,
|
||||
"reveal checkbox should be checked if MP dialog authenticated"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -43,7 +43,7 @@ add_task(async function test_query_parameter_filter() {
|
||||
await ContentTask.spawn(browser, vanillaLogins, async logins => {
|
||||
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return loginList._logins.length == 2;
|
||||
return loginList._loginGuidsSortedOrder.length == 2;
|
||||
}, "Waiting for logins to be cached");
|
||||
|
||||
let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
|
||||
@ -82,9 +82,9 @@ add_task(async function test_query_parameter_filter() {
|
||||
logins[0].guid,
|
||||
"TEST_LOGIN1 should be visible"
|
||||
);
|
||||
is(hiddenLoginListItems.length, 1, "One login should be hidden");
|
||||
is(hiddenLoginListItems.length, 2, "One login should be hidden");
|
||||
is(
|
||||
hiddenLoginListItems[0].dataset.guid,
|
||||
hiddenLoginListItems[1].dataset.guid,
|
||||
logins[1].guid,
|
||||
"TEST_LOGIN2 should be hidden"
|
||||
);
|
||||
|
@ -19,7 +19,8 @@ add_task(async function test_show_logins() {
|
||||
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
|
||||
let loginFound = await ContentTaskUtils.waitForCondition(() => {
|
||||
return (
|
||||
loginList._logins.length == 1 && loginList._logins[0].guid == loginGuid
|
||||
loginList._loginGuidsSortedOrder.length == 1 &&
|
||||
loginList._loginGuidsSortedOrder[0] == loginGuid
|
||||
);
|
||||
}, "Waiting for login to be displayed");
|
||||
ok(loginFound, "Stored logins should be displayed upon loading the page");
|
||||
@ -63,14 +64,38 @@ add_task(async function test_login_item() {
|
||||
usernameInput.value += "-undome";
|
||||
passwordInput.value += "-undome";
|
||||
|
||||
let dialog = content.document.querySelector("confirmation-dialog");
|
||||
ok(dialog.hidden, "Confirm dialog should initially be hidden");
|
||||
|
||||
let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
|
||||
cancelButton.click();
|
||||
|
||||
ok(!dialog.hidden, "Confirm dialog should be visible");
|
||||
|
||||
let confirmDiscardButton = dialog.shadowRoot.querySelector(
|
||||
".confirm-button"
|
||||
);
|
||||
await content.document.l10n.translateElements([
|
||||
dialog.shadowRoot.querySelector(".title"),
|
||||
dialog.shadowRoot.querySelector(".message"),
|
||||
confirmDiscardButton,
|
||||
]);
|
||||
|
||||
confirmDiscardButton.click();
|
||||
|
||||
ok(dialog.hidden, "Confirm dialog should be hidden after confirming");
|
||||
|
||||
usernameInput = loginItem.shadowRoot.querySelector(
|
||||
"input[name='username']"
|
||||
);
|
||||
passwordInput = loginItem.shadowRoot.querySelector(
|
||||
"input[name='password']"
|
||||
);
|
||||
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => usernameInput.value == login.username
|
||||
);
|
||||
|
||||
is(
|
||||
usernameInput.value,
|
||||
login.username,
|
||||
@ -103,9 +128,10 @@ add_task(async function test_login_item() {
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
loginListItem = Cu.waiveXrays(
|
||||
loginList.shadowRoot.querySelector(".login-list-item")
|
||||
loginList.shadowRoot.querySelector(".login-list-item[data-guid]")
|
||||
);
|
||||
return (
|
||||
loginListItem._login &&
|
||||
loginListItem._login.username == usernameInput.value &&
|
||||
loginListItem._login.password == passwordInput.value
|
||||
);
|
||||
@ -123,7 +149,7 @@ add_task(async function test_login_item() {
|
||||
let deleteButton = loginItem.shadowRoot.querySelector(".delete-button");
|
||||
deleteButton.click();
|
||||
let confirmDeleteDialog = Cu.waiveXrays(
|
||||
content.document.querySelector("confirm-delete-dialog")
|
||||
content.document.querySelector("confirmation-dialog")
|
||||
);
|
||||
let confirmDeleteButton = confirmDeleteDialog.shadowRoot.querySelector(
|
||||
".confirm-button"
|
||||
@ -131,8 +157,8 @@ add_task(async function test_login_item() {
|
||||
confirmDeleteButton.click();
|
||||
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
loginListItem = Cu.waiveXrays(
|
||||
loginList.shadowRoot.querySelector(".login-list-item")
|
||||
loginListItem = loginList.shadowRoot.querySelector(
|
||||
".login-list-item[data-guid]"
|
||||
);
|
||||
return !loginListItem;
|
||||
}, "Waiting for login to be removed from list");
|
||||
|
@ -45,3 +45,20 @@ Object.defineProperty(document, "l10n", {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(window, "AboutLoginsUtils", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
promptForMasterPassword(resolve) {
|
||||
resolve(true);
|
||||
},
|
||||
doLoginsMatch(login1, login2) {
|
||||
return (
|
||||
login1.origin == login2.origin &&
|
||||
login1.username == login2.username &&
|
||||
login1.password == login2.password
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -1,14 +1,14 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test the confirm-delete-dialog component
|
||||
Test the confirmation-dialog component
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the confirm-delete-dialog component</title>
|
||||
<title>Test the confirmation-dialog component</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/confirm-delete-dialog.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.js"></script>
|
||||
<script src="aboutlogins_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
@ -23,52 +23,70 @@ Test the confirm-delete-dialog component
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script>
|
||||
/** Test the confirm-delete-dialog component **/
|
||||
/** Test the confirmation-dialog component **/
|
||||
|
||||
let cancelButton, confirmButton, gConfirmDeleteDialog;
|
||||
let options = {
|
||||
title: "confirm-delete-dialog-title",
|
||||
message: "confirm-delete-dialog-message",
|
||||
confirmButtonLabel: "confirm-delete-dialog-confirm-button"
|
||||
};
|
||||
let cancelButton, confirmButton, gConfirmationDialog;
|
||||
add_task(async function setup() {
|
||||
let templateFrame = document.getElementById("templateFrame");
|
||||
let displayEl = document.getElementById("display");
|
||||
importDependencies(templateFrame, displayEl);
|
||||
|
||||
gConfirmDeleteDialog = document.createElement("confirm-delete-dialog");
|
||||
displayEl.appendChild(gConfirmDeleteDialog);
|
||||
ok(gConfirmDeleteDialog, "The dialog should exist");
|
||||
gConfirmationDialog = document.createElement("confirmation-dialog");
|
||||
displayEl.appendChild(gConfirmationDialog);
|
||||
ok(gConfirmationDialog, "The dialog should exist");
|
||||
|
||||
cancelButton = gConfirmDeleteDialog.shadowRoot.querySelector(".cancel-button");
|
||||
confirmButton = gConfirmDeleteDialog.shadowRoot.querySelector(".confirm-button");
|
||||
cancelButton = gConfirmationDialog.shadowRoot.querySelector(".cancel-button");
|
||||
confirmButton = gConfirmationDialog.shadowRoot.querySelector(".confirm-button");
|
||||
ok(cancelButton, "The cancel button should exist");
|
||||
ok(confirmButton, "The confirm button should exist");
|
||||
});
|
||||
|
||||
add_task(async function test_escape_key_to_cancel() {
|
||||
gConfirmDeleteDialog.show();
|
||||
ok(!gConfirmDeleteDialog.hidden, "The dialog should be visible");
|
||||
gConfirmationDialog.show(options);
|
||||
ok(!gConfirmationDialog.hidden, "The dialog should be visible");
|
||||
sendKey("ESCAPE");
|
||||
ok(gConfirmDeleteDialog.hidden, "The dialog should be hidden after hitting Escape");
|
||||
gConfirmDeleteDialog.hide();
|
||||
ok(gConfirmationDialog.hidden, "The dialog should be hidden after hitting Escape");
|
||||
gConfirmationDialog.hide();
|
||||
});
|
||||
|
||||
add_task(async function test_initial_focus() {
|
||||
gConfirmDeleteDialog.show();
|
||||
ok(!gConfirmDeleteDialog.hidden, "The dialog should be visible");
|
||||
is(gConfirmDeleteDialog.shadowRoot.activeElement, cancelButton,
|
||||
"After initially opening the dialog, the cancel button should be focused");
|
||||
gConfirmDeleteDialog.hide();
|
||||
gConfirmationDialog.show(options);
|
||||
ok(!gConfirmationDialog.hidden, "The dialog should be visible");
|
||||
is(gConfirmationDialog.shadowRoot.activeElement, confirmButton,
|
||||
"After initially opening the dialog, the confirm button should be focused");
|
||||
gConfirmationDialog.hide();
|
||||
});
|
||||
|
||||
add_task(async function test_tab_focus() {
|
||||
gConfirmDeleteDialog.show();
|
||||
ok(!gConfirmDeleteDialog.hidden, "The dialog should be visible");
|
||||
gConfirmationDialog.show(options);
|
||||
ok(!gConfirmationDialog.hidden, "The dialog should be visible");
|
||||
sendKey("TAB");
|
||||
is(gConfirmDeleteDialog.shadowRoot.activeElement, confirmButton,
|
||||
"After opening the dialog and tabbing once, the confirm delete button should be focused");
|
||||
gConfirmDeleteDialog.hide();
|
||||
is(gConfirmationDialog.shadowRoot.activeElement, cancelButton,
|
||||
"After opening the dialog and tabbing once, the cancel button should be focused");
|
||||
gConfirmationDialog.hide();
|
||||
});
|
||||
|
||||
add_task(async function test_enter_key_to_cancel() {
|
||||
let showPromise = gConfirmDeleteDialog.show();
|
||||
ok(!gConfirmDeleteDialog.hidden, "The dialog should be visible");
|
||||
let showPromise = gConfirmationDialog.show(options);
|
||||
ok(!gConfirmationDialog.hidden, "The dialog should be visible");
|
||||
sendKey("RETURN");
|
||||
try {
|
||||
await showPromise;
|
||||
ok(true, "The dialog Promise should resolve after hitting Return with the confirm button focused");
|
||||
} catch (ex) {
|
||||
ok(false, "The dialog Promise should not reject after hitting Return with the confirm button focused");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_enter_key_to_confirm() {
|
||||
let showPromise = gConfirmationDialog.show(options);
|
||||
ok(!gConfirmationDialog.hidden, "The dialog should be visible");
|
||||
sendKey("TAB");
|
||||
sendKey("RETURN");
|
||||
try {
|
||||
await showPromise;
|
||||
@ -78,18 +96,32 @@ add_task(async function test_enter_key_to_cancel() {
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_enter_key_to_confirm() {
|
||||
let showPromise = gConfirmDeleteDialog.show();
|
||||
ok(!gConfirmDeleteDialog.hidden, "The dialog should be visible");
|
||||
sendKey("TAB");
|
||||
sendKey("RETURN");
|
||||
try {
|
||||
await showPromise;
|
||||
ok(true, "The dialog Promise should resolve after hitting Return with the confirm button focused");
|
||||
} catch (ex) {
|
||||
ok(false, "The dialog Promise should not reject after hitting Return with the confirm button focused");
|
||||
add_task(async function test_dialog_focus_trap() {
|
||||
let displayEl = document.getElementById("display");
|
||||
let displayElChildSpan = document.createElement("span");
|
||||
displayElChildSpan.tabIndex = 0;
|
||||
displayElChildSpan.id = "display-child";
|
||||
displayEl.appendChild(displayElChildSpan);
|
||||
|
||||
gConfirmationDialog.show(options);
|
||||
|
||||
ok(!gConfirmationDialog.hidden, "The dialog should be visible");
|
||||
ok(displayElChildSpan.tabIndex === -1, "The tabIndex value for elements with a hardcoded tabIndex attribute should be reset to '-1'.")
|
||||
ok(displayElChildSpan.dataset.oldTabIndex === "0", "Existing tabIndex values should be stored in `dataset.oldTabIndex`.")
|
||||
|
||||
const isActiveElemDialogOrHTML = (elemTagName) => {
|
||||
return (["HTML", "CONFIRMATION-DIALOG"].includes(elemTagName));
|
||||
}
|
||||
|
||||
let iterator = 0;
|
||||
while(iterator < 20) {
|
||||
sendKey("TAB");
|
||||
isnot(document.activeElement.id, "display-child", "The display-child element should not gain focus when the dialog is showing");
|
||||
is(isActiveElemDialogOrHTML(document.activeElement.tagName), true, "The confirmation-dialog should always have focus when the dialog is showing");
|
||||
iterator++;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -9,6 +9,7 @@ Test the login-item component
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/login-item.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.js"></script>
|
||||
<script src="aboutlogins_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
@ -25,7 +26,7 @@ Test the login-item component
|
||||
<script>
|
||||
/** Test the login-item component **/
|
||||
|
||||
let gLoginItem;
|
||||
let gLoginItem, gConfirmationDialog;
|
||||
const TEST_LOGIN_1 = {
|
||||
guid: "123456789",
|
||||
origin: "https://example.com",
|
||||
@ -53,6 +54,10 @@ add_task(async function setup() {
|
||||
|
||||
gLoginItem = document.createElement("login-item");
|
||||
displayEl.appendChild(gLoginItem);
|
||||
|
||||
gConfirmationDialog = document.createElement("confirmation-dialog");
|
||||
gConfirmationDialog.hidden = true;
|
||||
displayEl.appendChild(gConfirmationDialog);
|
||||
});
|
||||
|
||||
add_task(async function test_empty_item() {
|
||||
@ -77,6 +82,8 @@ add_task(async function test_set_login() {
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-created")).args.timeCreated, TEST_LOGIN_1.timeCreated, "time-created should be populated");
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-changed")).args.timeChanged, TEST_LOGIN_1.timePasswordChanged, "time-changed should be populated");
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-used")).args.timeUsed, TEST_LOGIN_1.timeLastUsed, "time-used should be populated");
|
||||
let copyButtons = [...gLoginItem.shadowRoot.querySelectorAll(".copy-button")];
|
||||
ok(copyButtons.every(button => !isHidden(button)), "The copy buttons should be visible when viewing a login");
|
||||
});
|
||||
|
||||
add_task(async function test_edit_login() {
|
||||
@ -95,6 +102,8 @@ add_task(async function test_edit_login() {
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-created")).args.timeCreated, TEST_LOGIN_1.timeCreated, "time-created should be populated");
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-changed")).args.timeChanged, TEST_LOGIN_1.timePasswordChanged, "time-changed should be populated");
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-used")).args.timeUsed, TEST_LOGIN_1.timeLastUsed, "time-used should be populated");
|
||||
let copyButtons = [...gLoginItem.shadowRoot.querySelectorAll(".copy-button")];
|
||||
ok(copyButtons.every(button => isHidden(button)), "The copy buttons should be visible when editing a login");
|
||||
|
||||
gLoginItem.shadowRoot.querySelector("input[name='username']").value = "newUsername";
|
||||
gLoginItem.shadowRoot.querySelector("input[name='password']").value = "newPassword";
|
||||
@ -120,6 +129,13 @@ add_task(async function test_edit_login_cancel() {
|
||||
"loginItem should not be in 'isNewLogin' mode");
|
||||
|
||||
gLoginItem.shadowRoot.querySelector(".cancel-button").click();
|
||||
gConfirmationDialog.shadowRoot.querySelector(".confirm-button").click();
|
||||
|
||||
await SimpleTest.promiseWaitForCondition(
|
||||
() => gConfirmationDialog.hidden,
|
||||
"waiting for confirmation dialog to hide"
|
||||
);
|
||||
|
||||
ok(!gLoginItem.dataset.editing, "loginItem should not be in 'edit' mode");
|
||||
ok(!gLoginItem.dataset.isNewLogin, "loginItem should not be in 'isNewLogin' mode");
|
||||
});
|
||||
@ -133,6 +149,8 @@ add_task(async function test_reveal_password_change_selected_login() {
|
||||
is(passwordInput.type, "password", "Password should be masked by default");
|
||||
revealCheckbox.click();
|
||||
ok(revealCheckbox.checked, "reveal-checkbox should be checked after clicking");
|
||||
await SimpleTest.promiseWaitForCondition(() => passwordInput.type == "text",
|
||||
"waiting for password input type to change after checking for master password");
|
||||
is(passwordInput.type, "text", "Password should be unmasked when checkbox is clicked");
|
||||
|
||||
gLoginItem.setLogin(TEST_LOGIN_2);
|
||||
@ -155,6 +173,8 @@ add_task(async function test_set_login_empty() {
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-created")).args.timeCreated, "", "time-created should be blank when undefined");
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-changed")).args.timeChanged, "", "time-changed should be blank when undefined");
|
||||
is(document.l10n.getAttributes(gLoginItem.shadowRoot.querySelector(".time-used")).args.timeUsed, "", "time-used should be blank when undefined");
|
||||
let copyButtons = [...gLoginItem.shadowRoot.querySelectorAll(".copy-button")];
|
||||
ok(copyButtons.every(button => isHidden(button)), "The copy buttons should be hidden when creating a login");
|
||||
|
||||
let createEventDispatched = false;
|
||||
document.addEventListener("AboutLoginsCreateLogin", event => {
|
||||
|
@ -93,21 +93,21 @@ add_task(async function test_keyboard_navigation() {
|
||||
|
||||
for (let [keyFwd, keyRev] of [["LEFT", "RIGHT"], ["DOWN", "UP"]]) {
|
||||
sendKey(keyFwd);
|
||||
await SimpleTest.promiseWaitForCondition(() => ol.getAttribute("aria-activedescendant") == ol.children[1].id,
|
||||
await SimpleTest.promiseWaitForCondition(() => ol.getAttribute("aria-activedescendant") == ol.querySelectorAll(".login-list-item:not([hidden])")[1].id,
|
||||
`waiting for second item in list to get focused (${keyFwd})`);
|
||||
ok(ol.children[1].classList.contains("keyboard-selected"), `second item should be marked as keyboard-selected (${keyFwd})`);
|
||||
ok(ol.querySelectorAll(".login-list-item:not([hidden])")[1].classList.contains("keyboard-selected"), `second item should be marked as keyboard-selected (${keyFwd})`);
|
||||
|
||||
sendKey(keyRev);
|
||||
await SimpleTest.promiseWaitForCondition(() => ol.getAttribute("aria-activedescendant") == ol.children[0].id,
|
||||
await SimpleTest.promiseWaitForCondition(() => ol.getAttribute("aria-activedescendant") == ol.querySelectorAll(".login-list-item:not([hidden])")[0].id,
|
||||
`waiting for first item in list to get focused (${keyRev})`);
|
||||
ok(ol.children[0].classList.contains("keyboard-selected"), `first item should be marked as keyboard-selected (${keyRev})`);
|
||||
ok(ol.querySelectorAll(".login-list-item:not([hidden])")[0].classList.contains("keyboard-selected"), `first item should be marked as keyboard-selected (${keyRev})`);
|
||||
}
|
||||
|
||||
sendKey("DOWN");
|
||||
await SimpleTest.promiseWaitForCondition(() => ol.getAttribute("aria-activedescendant") == ol.children[1].id,
|
||||
await SimpleTest.promiseWaitForCondition(() => ol.getAttribute("aria-activedescendant") == ol.querySelectorAll(".login-list-item:not([hidden])")[1].id,
|
||||
`waiting for second item in list to get focused (DOWN)`);
|
||||
ok(ol.children[1].classList.contains("keyboard-selected"), `second item should be marked as keyboard-selected (DOWN)`);
|
||||
let selectedGuid = ol.children[1].dataset.guid;
|
||||
ok(ol.querySelectorAll(".login-list-item:not([hidden])")[1].classList.contains("keyboard-selected"), `second item should be marked as keyboard-selected (DOWN)`);
|
||||
let selectedGuid = ol.querySelectorAll(".login-list-item:not([hidden])")[1].dataset.guid;
|
||||
|
||||
let loginSelectedEvent = null;
|
||||
gLoginList.addEventListener("AboutLoginsLoginSelected", event => loginSelectedEvent = event, {once: true});
|
||||
@ -124,7 +124,7 @@ add_task(async function test_empty_login_username_in_list() {
|
||||
}));
|
||||
|
||||
gLoginList.setLogins([TEST_LOGIN_3]);
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
is(loginListItems.length, 1, "The one stored login should be displayed");
|
||||
is(loginListItems[0].dataset.guid, TEST_LOGIN_3.guid, "login-list-item should have correct guid attribute");
|
||||
let loginUsername = loginListItems[0].querySelector(".username");
|
||||
@ -133,7 +133,7 @@ add_task(async function test_empty_login_username_in_list() {
|
||||
|
||||
add_task(async function test_populated_list() {
|
||||
gLoginList.setLogins([TEST_LOGIN_1, TEST_LOGIN_2]);
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
is(loginListItems.length, 2, "The two stored logins should be displayed");
|
||||
is(loginListItems[0].dataset.guid, TEST_LOGIN_1.guid, "login-list-item should have correct guid attribute");
|
||||
is(loginListItems[0].querySelector(".title").textContent, TEST_LOGIN_1.title,
|
||||
@ -143,7 +143,7 @@ add_task(async function test_populated_list() {
|
||||
ok(loginListItems[0].classList.contains("selected"), "The first item should be selected by default");
|
||||
ok(!loginListItems[1].classList.contains("selected"), "The second item should not be selected by default");
|
||||
loginListItems[0].click();
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
is(loginListItems.length, 2, "After selecting one, only the two stored logins should be displayed");
|
||||
ok(loginListItems[0].classList.contains("selected"), "The first item should be selected");
|
||||
ok(!loginListItems[1].classList.contains("selected"), "The second item should still not be selected");
|
||||
@ -159,7 +159,7 @@ add_task(async function test_filtered_list() {
|
||||
detail: "user1",
|
||||
}));
|
||||
is(JSON.parse(countSpan.getAttribute("data-l10n-args")).count, 1, "Count should match result amount");
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]");
|
||||
is(loginListItems[0].querySelector(".username").textContent, "user1", "user1 is expected first");
|
||||
ok(!loginListItems[0].hidden, "user1 should remain visible");
|
||||
ok(loginListItems[1].hidden, "user2 should be hidden");
|
||||
@ -169,7 +169,7 @@ add_task(async function test_filtered_list() {
|
||||
detail: "user2",
|
||||
}));
|
||||
is(JSON.parse(countSpan.getAttribute("data-l10n-args")).count, 1, "Count should match result amount");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]");
|
||||
ok(loginListItems[0].hidden, "user1 should be hidden");
|
||||
ok(!loginListItems[1].hidden, "user2 should be visible");
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
|
||||
@ -178,7 +178,7 @@ add_task(async function test_filtered_list() {
|
||||
detail: "user",
|
||||
}));
|
||||
is(JSON.parse(countSpan.getAttribute("data-l10n-args")).count, 2, "Count should match result amount");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]");
|
||||
ok(!loginListItems[0].hidden, "user1 should be visible");
|
||||
ok(!loginListItems[1].hidden, "user2 should be visible");
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
|
||||
@ -187,7 +187,7 @@ add_task(async function test_filtered_list() {
|
||||
detail: "foo",
|
||||
}));
|
||||
is(JSON.parse(countSpan.getAttribute("data-l10n-args")).count, 0, "Count should match result amount");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]");
|
||||
ok(loginListItems[0].hidden, "user1 should be hidden");
|
||||
ok(loginListItems[1].hidden, "user2 should be hidden");
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
|
||||
@ -196,7 +196,7 @@ add_task(async function test_filtered_list() {
|
||||
detail: "",
|
||||
}));
|
||||
is(JSON.parse(countSpan.getAttribute("data-l10n-args")).count, 2, "Count should be reset to full list length");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]");
|
||||
ok(!loginListItems[0].hidden, "user1 should be visible");
|
||||
ok(!loginListItems[1].hidden, "user2 should be visible");
|
||||
});
|
||||
@ -205,7 +205,7 @@ add_task(async function test_login_modified() {
|
||||
let modifiedLogin = Object.assign(TEST_LOGIN_1, {username: "user11"});
|
||||
gLoginList.loginModified(modifiedLogin);
|
||||
await asyncElementRendered();
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]:not([hidden])");
|
||||
is(loginListItems.length, 2, "Both logins should be displayed");
|
||||
is(loginListItems[0].dataset.guid, TEST_LOGIN_1.guid, "login-list-item should have correct guid attribute");
|
||||
is(loginListItems[0].querySelector(".title").textContent, TEST_LOGIN_1.title,
|
||||
@ -217,10 +217,14 @@ add_task(async function test_login_modified() {
|
||||
});
|
||||
|
||||
add_task(async function test_login_added() {
|
||||
let newLogin = Object.assign({}, TEST_LOGIN_1, {username: "user22", guid: "111222"});
|
||||
info("selected sort: " + gLoginList.shadowRoot.getElementById("login-sort").selectedIndex);
|
||||
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
is(loginListItems.length, 2, "Should have two logins at start of test");
|
||||
let newLogin = Object.assign({}, TEST_LOGIN_1, {title: "example2.example.com", guid: "111222"});
|
||||
gLoginList.loginAdded(newLogin);
|
||||
await asyncElementRendered();
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
is(loginListItems.length, 3, "New login should be added to the list");
|
||||
is(loginListItems[0].dataset.guid, TEST_LOGIN_1.guid, "login-list-item1 should have correct guid attribute");
|
||||
is(loginListItems[1].dataset.guid, TEST_LOGIN_2.guid, "login-list-item2 should have correct guid attribute");
|
||||
@ -234,7 +238,7 @@ add_task(async function test_login_added() {
|
||||
add_task(async function test_login_removed() {
|
||||
gLoginList.loginRemoved({guid: "111222"});
|
||||
await asyncElementRendered();
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
is(loginListItems.length, 2, "New login should be removed from the list");
|
||||
is(loginListItems[0].dataset.guid, TEST_LOGIN_1.guid, "login-list-item1 should have correct guid attribute");
|
||||
is(loginListItems[1].dataset.guid, TEST_LOGIN_2.guid, "login-list-item2 should have correct guid attribute");
|
||||
@ -242,18 +246,16 @@ add_task(async function test_login_removed() {
|
||||
|
||||
add_task(async function test_login_added_filtered() {
|
||||
let countSpan = gLoginList.shadowRoot.querySelector(".count");
|
||||
is(JSON.parse(countSpan.getAttribute("data-l10n-args")).count, 2, "Count should match full list length");
|
||||
is(document.l10n.getAttributes(countSpan).args.count, 2, "Count should match full list length");
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: "user1",
|
||||
}));
|
||||
is(JSON.parse(countSpan.getAttribute("data-l10n-args")).count, 1, "Count should match result amount");
|
||||
|
||||
let newLogin = Object.assign({}, TEST_LOGIN_1, {username: "user22", guid: "111222"});
|
||||
let newLogin = Object.assign({}, TEST_LOGIN_1, {title: "example2.example.com", username: "user22", guid: "111222"});
|
||||
gLoginList.loginAdded(newLogin);
|
||||
await asyncElementRendered();
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]");
|
||||
is(loginListItems.length, 3, "New login should be added to the list");
|
||||
is(loginListItems[0].dataset.guid, TEST_LOGIN_1.guid, "login-list-item1 should have correct guid attribute");
|
||||
is(loginListItems[1].dataset.guid, TEST_LOGIN_2.guid, "login-list-item2 should have correct guid attribute");
|
||||
@ -265,32 +267,65 @@ add_task(async function test_login_added_filtered() {
|
||||
});
|
||||
|
||||
add_task(async function test_sorted_list() {
|
||||
function dispatchChangeEvent(target) {
|
||||
let event = document.createEvent("UIEvent");
|
||||
event.initEvent("change", true, true);
|
||||
target.dispatchEvent(event);
|
||||
}
|
||||
|
||||
// Clear the filter
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsFilterLogins", {
|
||||
detail: "",
|
||||
}));
|
||||
|
||||
// Clear the selection so the 'new' login will be in the list too.
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsLoginSelected", {
|
||||
detail: {},
|
||||
}));
|
||||
|
||||
// sort by last used
|
||||
gLoginList.shadowRoot.getElementById("login-sort").selectedIndex = 1;
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
is(loginListItems.length, 3, "The list should contain the three stored logins");
|
||||
let timeUsed = loginListItems[0]._login.timeLastUsed;
|
||||
let timeUsed2 = loginListItems[1]._login.timeLastUsed;
|
||||
is(timeUsed2 > timeUsed, true, "Last used login should be displayed at top of list");
|
||||
// make sure that the logins have distinct orderings based on sort order
|
||||
let [guid1, guid2, guid3] = gLoginList._loginGuidsSortedOrder;
|
||||
gLoginList._logins[guid1].login.timeLastUsed = 0;
|
||||
gLoginList._logins[guid2].login.timeLastUsed = 1;
|
||||
gLoginList._logins[guid3].login.timeLastUsed = 2;
|
||||
gLoginList._logins[guid1].login.title = "a";
|
||||
gLoginList._logins[guid2].login.title = "b";
|
||||
gLoginList._logins[guid3].login.title = "c";
|
||||
gLoginList._logins[guid1].login.timePasswordChanged = 1;
|
||||
gLoginList._logins[guid2].login.timePasswordChanged = 2;
|
||||
gLoginList._logins[guid3].login.timePasswordChanged = 0;
|
||||
|
||||
// sort by name
|
||||
gLoginList.shadowRoot.getElementById("login-sort").selectedIndex = 0;
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let title = loginListItems[0]._login.title;
|
||||
// sort by last used
|
||||
let loginSort = gLoginList.shadowRoot.getElementById("login-sort");
|
||||
loginSort.selectedIndex = 1;
|
||||
dispatchChangeEvent(loginSort);
|
||||
let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
is(loginListItems.length, 3, "The list should contain the three stored logins");
|
||||
let timeUsed1 = loginListItems[0]._login.timeLastUsed;
|
||||
let timeUsed2 = loginListItems[1]._login.timeLastUsed;
|
||||
let timeUsed3 = loginListItems[2]._login.timeLastUsed;
|
||||
is(timeUsed1 > timeUsed2, true, "Logins sorted by timeLastUsed. First: " + timeUsed1 + "; Second: " + timeUsed2);
|
||||
is(timeUsed2 > timeUsed3, true, "Logins sorted by timeLastUsed. Second: " + timeUsed2 + "; Third: " + timeUsed3);
|
||||
|
||||
// sort by title
|
||||
loginSort.selectedIndex = 0;
|
||||
dispatchChangeEvent(loginSort);
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
let title1 = loginListItems[0]._login.title;
|
||||
let title2 = loginListItems[1]._login.title;
|
||||
is(title.localeCompare(title2), -1, "Logins should be sorted alphabetically by hostname");
|
||||
let title3 = loginListItems[2]._login.title;
|
||||
is(title1.localeCompare(title2), -1, "Logins sorted by title. First: " + title1 + "; Second: " + title2);
|
||||
is(title2.localeCompare(title3), -1, "Logins sorted by title. Second: " + title2 + "; Third: " + title3);
|
||||
|
||||
// sort by last changed
|
||||
gLoginList.shadowRoot.getElementById("login-sort").selectedIndex = 2;
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item");
|
||||
let pwChanged = loginListItems[0]._login.timePasswordChanged;
|
||||
loginSort.selectedIndex = 2;
|
||||
dispatchChangeEvent(loginSort);
|
||||
loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not([hidden])");
|
||||
let pwChanged1 = loginListItems[0]._login.timePasswordChanged;
|
||||
let pwChanged2 = loginListItems[1]._login.timePasswordChanged;
|
||||
is(pwChanged2 > pwChanged, true, "Login with most recently changed password should be displayed at top of list");
|
||||
let pwChanged3 = loginListItems[2]._login.timePasswordChanged;
|
||||
is(pwChanged1 > pwChanged2, true, "Logins sorted by timePasswordChanged. First: " + pwChanged1 + "; Second: " + pwChanged2);
|
||||
is(pwChanged2 > pwChanged3, true, "Logins sorted by timePasswordChanged. Second: " + pwChanged2 + "; Third: " + pwChanged3);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -18,57 +18,6 @@ async function openTabInUserContext(uri, userContextId) {
|
||||
return { tab, browser };
|
||||
}
|
||||
|
||||
// Opens `uri' in a new <iframe mozbrowser> with the provided userContextId.
|
||||
// Returns the newly opened browser.
|
||||
async function addBrowserFrameInUserContext(uri, userContextId) {
|
||||
// Create a browser frame with the user context and uri
|
||||
const browser = document.createElementNS(
|
||||
"http://www.w3.org/1999/xhtml",
|
||||
"iframe"
|
||||
);
|
||||
browser.setAttribute("remote", "true");
|
||||
browser.setAttribute("usercontextid", userContextId);
|
||||
browser.setAttribute("mozbrowser", "true");
|
||||
// `noisolation = true` means `OA.mInIsolatedMozBrowser = false` which matches
|
||||
// the default for a XUL browser. It is indepedent from user contexts.
|
||||
browser.setAttribute("noisolation", "true");
|
||||
browser.setAttribute("src", uri);
|
||||
gBrowser.tabpanels.appendChild(browser);
|
||||
|
||||
// Create a XUL browser-like API expected by test helpers
|
||||
Object.defineProperty(browser, "messageManager", {
|
||||
get() {
|
||||
return browser.frameLoader.messageManager;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
await browserFrameLoaded(browser);
|
||||
|
||||
return { browser };
|
||||
}
|
||||
|
||||
function browserFrameLoaded(browser) {
|
||||
const mm = browser.messageManager;
|
||||
return new Promise(resolve => {
|
||||
const eventName = "browser-test-utils:loadEvent";
|
||||
mm.addMessageListener(eventName, function onLoad(msg) {
|
||||
if (msg.target != browser) {
|
||||
return;
|
||||
}
|
||||
mm.removeMessageListener(eventName, onLoad);
|
||||
resolve(msg.data.internalURL);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeBrowserFrame({ browser }) {
|
||||
browser.remove();
|
||||
// Clean up Browser API parent-side data
|
||||
delete window._browserElementParents;
|
||||
}
|
||||
|
||||
async function runTestForReceiver(receiver) {
|
||||
let channelName = "contextualidentity-broadcastchannel";
|
||||
|
||||
@ -132,13 +81,3 @@ add_task(async function test() {
|
||||
await runTestForReceiver(receiver);
|
||||
gBrowser.removeTab(receiver.tab);
|
||||
});
|
||||
|
||||
add_task(async function test() {
|
||||
info("Checking broadcast channel with <iframe mozbrowser> receiver");
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["dom.mozBrowserFramesEnabled", true]],
|
||||
});
|
||||
let receiver = await addBrowserFrameInUserContext(URI, 2);
|
||||
await runTestForReceiver(receiver);
|
||||
removeBrowserFrame(receiver);
|
||||
});
|
||||
|
@ -988,14 +988,6 @@ var PanelMultiView = class extends AssociatedToNode {
|
||||
this._viewStack.style.marginInlineStart = "-" + deltaX + "px";
|
||||
}
|
||||
|
||||
// Set the transition style and listen for its end to clean up and make sure
|
||||
// the box sizing becomes dynamic again.
|
||||
// Somehow, putting these properties in PanelUI.css doesn't work for newly
|
||||
// shown nodes in a XUL parent node.
|
||||
this._viewStack.style.transition =
|
||||
"transform var(--animation-easing-function)" +
|
||||
" var(--panelui-subview-transition-duration)";
|
||||
this._viewStack.style.willChange = "transform";
|
||||
// Use an outline instead of a border so that the size is not affected.
|
||||
deepestNode.style.outline = "1px solid var(--panel-separator-color)";
|
||||
|
||||
@ -1022,41 +1014,6 @@ var PanelMultiView = class extends AssociatedToNode {
|
||||
this._viewStack.style.transform =
|
||||
"translateX(" + (moveToLeft ? "" : "-") + deltaX + "px)";
|
||||
|
||||
await new Promise(resolve => {
|
||||
details.resolve = resolve;
|
||||
this._viewContainer.addEventListener(
|
||||
"transitionend",
|
||||
(details.listener = ev => {
|
||||
// It's quite common that `height` on the view container doesn't need
|
||||
// to transition, so we make sure to do all the work on the transform
|
||||
// transition-end, because that is guaranteed to happen.
|
||||
if (ev.target != this._viewStack || ev.propertyName != "transform") {
|
||||
return;
|
||||
}
|
||||
this._viewContainer.removeEventListener(
|
||||
"transitionend",
|
||||
details.listener
|
||||
);
|
||||
delete details.listener;
|
||||
resolve();
|
||||
})
|
||||
);
|
||||
this._viewContainer.addEventListener(
|
||||
"transitioncancel",
|
||||
(details.cancelListener = ev => {
|
||||
if (ev.target != this._viewStack) {
|
||||
return;
|
||||
}
|
||||
this._viewContainer.removeEventListener(
|
||||
"transitioncancel",
|
||||
details.cancelListener
|
||||
);
|
||||
delete details.cancelListener;
|
||||
resolve();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Bail out if the panel was closed during the transition.
|
||||
if (!nextPanelView.isOpenIn(this)) {
|
||||
return;
|
||||
|
@ -42,7 +42,6 @@ skip-if = os == "linux"
|
||||
skip-if = os == "mac"
|
||||
|
||||
[browser_934951_zoom_in_toolbar.js]
|
||||
uses-unsafe-cpows = true
|
||||
[browser_938980_navbar_collapsed.js]
|
||||
[browser_938995_indefaultstate_nonremovable.js]
|
||||
[browser_940013_registerToolbarNode_calls_registerArea.js]
|
||||
|
@ -14,12 +14,6 @@ let { SyncedTabs } = ChromeUtils.import(
|
||||
);
|
||||
let { UIState } = ChromeUtils.import("resource://services-sync/UIState.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"UITour",
|
||||
"resource:///modules/UITour.jsm"
|
||||
);
|
||||
|
||||
// These are available on the widget implementation, but it seems impossible
|
||||
// to grab that impl at runtime.
|
||||
const DECKINDEX_TABS = 0;
|
||||
@ -97,11 +91,6 @@ async function openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
|
||||
// check the button's functionality
|
||||
await document.getElementById("nav-bar").overflowable.show();
|
||||
|
||||
if (entryPoint == "uitour") {
|
||||
UITour.tourBrowsersByWindow.set(window, new Set());
|
||||
UITour.tourBrowsersByWindow.get(window).add(gBrowser.selectedBrowser);
|
||||
}
|
||||
|
||||
let syncButton = document.getElementById("sync-button");
|
||||
ok(syncButton, "The Sync button was added to the Panel Menu");
|
||||
|
||||
@ -164,7 +153,6 @@ async function asyncCleanup() {
|
||||
// restore the tabs
|
||||
BrowserTestUtils.addTab(gBrowser, initialLocation);
|
||||
gBrowser.removeTab(newTab);
|
||||
UITour.tourBrowsersByWindow.delete(window);
|
||||
}
|
||||
|
||||
// When Sync is not setup.
|
||||
|
@ -15,15 +15,10 @@ body {
|
||||
}
|
||||
|
||||
#sectionTitle {
|
||||
float: left;
|
||||
float: inline-start;
|
||||
padding-inline-start: 1rem;
|
||||
}
|
||||
|
||||
#sectionTitle:dir(rtl) {
|
||||
float: right;
|
||||
padding-inline-end: 1rem;
|
||||
}
|
||||
|
||||
/** Categories **/
|
||||
|
||||
.category {
|
||||
|
@ -22,7 +22,7 @@ body * {
|
||||
}
|
||||
|
||||
.browser-style {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
margin-bottom: 6px;
|
||||
text-align: left;
|
||||
}
|
||||
@ -145,7 +145,7 @@ button.browser-style.default.focused {
|
||||
|
||||
/* Radio Buttons */
|
||||
.browser-style > input[type="radio"] {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
background-position: center;
|
||||
border: 1px solid #b1b1b1;
|
||||
@ -199,7 +199,7 @@ button.browser-style.default.focused {
|
||||
|
||||
/* Checkboxes */
|
||||
.browser-style > input[type="checkbox"] {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
background-position: center;
|
||||
border: 1px solid #b1b1b1;
|
||||
@ -390,8 +390,7 @@ textarea.browser-style:invalid:not(:focus) {
|
||||
|
||||
.panel-list-item:not(.disabled):hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-block: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.panel-list-item:not(.disabled):hover:active {
|
||||
|
@ -264,11 +264,6 @@ add_task(async function() {
|
||||
await extension.awaitMessage("set-synchronous-set");
|
||||
|
||||
let text = await startInputSession();
|
||||
if (!UrlbarPrefs.get("quantumbar")) {
|
||||
// TODO Bug 1530338: We can't yet wait for a specific result for the
|
||||
// quantumbar. Therefore we just skip this for now.
|
||||
await waitForResult(0);
|
||||
}
|
||||
|
||||
extension.sendMessage(info.test);
|
||||
await extension.awaitMessage("test-ready");
|
||||
|
@ -48,22 +48,11 @@ add_task(async function test_tab_options_modals() {
|
||||
aboutAddonsBrowser.addEventListener(
|
||||
"DOMWillOpenModalDialog",
|
||||
function onModalDialog(event) {
|
||||
if (Cu.isCrossProcessWrapper(event.target)) {
|
||||
// This event fires in both the content and chrome processes. We
|
||||
// want to ignore the one in the content process.
|
||||
return;
|
||||
}
|
||||
|
||||
aboutAddonsBrowser.removeEventListener(
|
||||
"DOMWillOpenModalDialog",
|
||||
onModalDialog,
|
||||
true
|
||||
);
|
||||
// Wait for the next event tick to make sure the remaining part of the
|
||||
// testcase runs after the dialog gets opened.
|
||||
SimpleTest.executeSoon(resolve);
|
||||
},
|
||||
true
|
||||
{ once: true, capture: true }
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
||||
oncommand="MigrationWizard.onImportItemCommand();">
|
||||
<description control="dataSources">&importItems.label;</description>
|
||||
|
||||
<vbox id="dataSources" style="overflow: auto; -moz-appearance: listbox" align="left" flex="1" role="group"/>
|
||||
<vbox id="dataSources" style="overflow: auto; appearance: auto; -moz-default-appearance: listbox" align="left" flex="1" role="group"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="migrating" pageid="migrating" label="&migrating.title;"
|
||||
|
@ -9,9 +9,6 @@ const { actionTypes: at } = ChromeUtils.import(
|
||||
const { getDomain } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/TippyTopProvider.jsm"
|
||||
);
|
||||
const { RemoteSettings } = ChromeUtils.import(
|
||||
"resource://services-settings/remote-settings.js"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
@ -148,50 +145,10 @@ this.FaviconFeed = class FaviconFeed {
|
||||
return;
|
||||
}
|
||||
|
||||
const site = await this.getSite(getDomain(url));
|
||||
if (!site) {
|
||||
if (!this._queryForRedirects.has(url)) {
|
||||
this._queryForRedirects.add(url);
|
||||
Services.tm.idleDispatchToMainThread(() => fetchIconFromRedirects(url));
|
||||
}
|
||||
return;
|
||||
if (!this._queryForRedirects.has(url)) {
|
||||
this._queryForRedirects.add(url);
|
||||
Services.tm.idleDispatchToMainThread(() => fetchIconFromRedirects(url));
|
||||
}
|
||||
|
||||
let iconUri = Services.io.newURI(site.image_url);
|
||||
// The #tippytop is to be able to identify them for telemetry.
|
||||
iconUri = iconUri
|
||||
.mutate()
|
||||
.setRef("tippytop")
|
||||
.finalize();
|
||||
PlacesUtils.favicons.setAndFetchFaviconForPage(
|
||||
Services.io.newURI(url),
|
||||
iconUri,
|
||||
false,
|
||||
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
|
||||
null,
|
||||
Services.scriptSecurityManager.getSystemPrincipal()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site tippy top data from Remote Settings.
|
||||
*/
|
||||
async getSite(domain) {
|
||||
const sites = await this.tippyTop.get({
|
||||
filters: { domain },
|
||||
syncIfEmpty: false,
|
||||
});
|
||||
return sites.length ? sites[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tippy top collection from Remote Settings.
|
||||
*/
|
||||
get tippyTop() {
|
||||
if (!this._tippyTop) {
|
||||
this._tippyTop = RemoteSettings("tippytop");
|
||||
}
|
||||
return this._tippyTop;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,463 +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 { RemoteSettings } = ChromeUtils.import(
|
||||
"resource://services-settings/remote-settings.js"
|
||||
);
|
||||
|
||||
const { actionCreators: ac } = ChromeUtils.import(
|
||||
"resource://activity-stream/common/Actions.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"perfService",
|
||||
"resource://activity-stream/common/PerfService.jsm"
|
||||
);
|
||||
|
||||
const { NaiveBayesTextTagger } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/NaiveBayesTextTagger.jsm"
|
||||
);
|
||||
const { NmfTextTagger } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/NmfTextTagger.jsm"
|
||||
);
|
||||
const { RecipeExecutor } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/RecipeExecutor.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm"
|
||||
);
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => new TextDecoder());
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "baseAttachmentsURL", async () => {
|
||||
const server = Services.prefs.getCharPref("services.settings.server");
|
||||
const serverInfo = await (await fetch(`${server}/`, {
|
||||
credentials: "omit",
|
||||
})).json();
|
||||
const {
|
||||
capabilities: {
|
||||
attachments: { base_url },
|
||||
},
|
||||
} = serverInfo;
|
||||
return base_url;
|
||||
});
|
||||
|
||||
const PERSONALITY_PROVIDER_DIR = OS.Path.join(
|
||||
OS.Constants.Path.localProfileDir,
|
||||
"personality-provider"
|
||||
);
|
||||
const RECIPE_NAME = "personality-provider-recipe";
|
||||
const MODELS_NAME = "personality-provider-models";
|
||||
|
||||
function getHash(aStr) {
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
let toHexString = charCode => `0${charCode.toString(16)}`.slice(-2);
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
|
||||
Ci.nsICryptoHash
|
||||
);
|
||||
hasher.init(Ci.nsICryptoHash.SHA256);
|
||||
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
|
||||
Ci.nsIStringInputStream
|
||||
);
|
||||
stringStream.data = aStr;
|
||||
hasher.updateFromStream(stringStream, -1);
|
||||
|
||||
// convert the binary hash data to a hex string.
|
||||
let binary = hasher.finish(false);
|
||||
return Array.from(binary, (c, i) => toHexString(binary.charCodeAt(i)))
|
||||
.join("")
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 provider builds and ranks an interest profile (also called an “interest vector”) off the browse history.
|
||||
* This allows Firefox to classify pages into topics, by examining the text found on the page.
|
||||
* It does this by looking at the history text content, title, and description.
|
||||
*/
|
||||
this.PersonalityProvider = class PersonalityProvider {
|
||||
constructor(
|
||||
timeSegments,
|
||||
parameterSets,
|
||||
maxHistoryQueryResults,
|
||||
version,
|
||||
scores,
|
||||
v2Params
|
||||
) {
|
||||
this.v2Params = v2Params || {};
|
||||
this.dispatch = this.v2Params.dispatch || (() => {});
|
||||
this.modelKeys = this.v2Params.modelKeys;
|
||||
this.timeSegments = timeSegments;
|
||||
this.parameterSets = parameterSets;
|
||||
this.maxHistoryQueryResults = maxHistoryQueryResults;
|
||||
this.version = version;
|
||||
this.scores = scores || {};
|
||||
this.interestConfig = this.scores.interestConfig;
|
||||
this.interestVector = this.scores.interestVector;
|
||||
this.onSync = this.onSync.bind(this);
|
||||
this.setupSyncAttachment(RECIPE_NAME);
|
||||
this.setupSyncAttachment(MODELS_NAME);
|
||||
}
|
||||
|
||||
async onSync(event) {
|
||||
const {
|
||||
data: { created, updated, deleted },
|
||||
} = event;
|
||||
|
||||
// Remove every removed attachment.
|
||||
const toRemove = deleted.concat(updated.map(u => u.old));
|
||||
await Promise.all(toRemove.map(record => this.deleteAttachment(record)));
|
||||
|
||||
// Download every new/updated attachment.
|
||||
const toDownload = created.concat(updated.map(u => u.new));
|
||||
await Promise.all(
|
||||
toDownload.map(record => this.maybeDownloadAttachment(record))
|
||||
);
|
||||
}
|
||||
|
||||
setupSyncAttachment(collection) {
|
||||
RemoteSettings(collection).on("sync", this.onSync);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the attachment to disk assuming the dir already exists
|
||||
* and any existing files matching the filename are clobbered.
|
||||
*/
|
||||
async _downloadAttachment(record) {
|
||||
const {
|
||||
attachment: { location, filename },
|
||||
} = record;
|
||||
const remoteFilePath = (await baseAttachmentsURL) + location;
|
||||
const localFilePath = OS.Path.join(PERSONALITY_PROVIDER_DIR, filename);
|
||||
const headers = new Headers();
|
||||
headers.set("Accept-Encoding", "gzip");
|
||||
const resp = await fetch(remoteFilePath, { headers, credentials: "omit" });
|
||||
if (!resp.ok) {
|
||||
Cu.reportError(`Failed to fetch ${remoteFilePath}: ${resp.status}`);
|
||||
return;
|
||||
}
|
||||
const buffer = await resp.arrayBuffer();
|
||||
const bytes = new Uint8Array(buffer);
|
||||
await OS.File.writeAtomic(localFilePath, bytes, {
|
||||
tmpPath: `${localFilePath}.tmp`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to download the attachment, but only if it doesn't already exist.
|
||||
*/
|
||||
async maybeDownloadAttachment(record, retries = 3) {
|
||||
const {
|
||||
attachment: { filename, hash, size },
|
||||
} = record;
|
||||
await OS.File.makeDir(PERSONALITY_PROVIDER_DIR);
|
||||
const localFilePath = OS.Path.join(PERSONALITY_PROVIDER_DIR, filename);
|
||||
|
||||
let retry = 0;
|
||||
while (
|
||||
retry++ < retries &&
|
||||
(!(await OS.File.exists(localFilePath)) ||
|
||||
(await OS.File.stat(localFilePath)).size !== size ||
|
||||
getHash(await this._getFileStr(localFilePath)) !== hash)
|
||||
) {
|
||||
await this._downloadAttachment(record);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAttachment(record) {
|
||||
const {
|
||||
attachment: { filename },
|
||||
} = record;
|
||||
await OS.File.makeDir(PERSONALITY_PROVIDER_DIR);
|
||||
const path = OS.Path.join(PERSONALITY_PROVIDER_DIR, filename);
|
||||
|
||||
await OS.File.remove(path, { ignoreAbsent: true });
|
||||
return OS.File.removeEmptyDir(PERSONALITY_PROVIDER_DIR, {
|
||||
ignoreAbsent: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets contents of the attachment if it already exists on file,
|
||||
* and if not attempts to download it.
|
||||
*/
|
||||
async getAttachment(record) {
|
||||
const {
|
||||
attachment: { filename },
|
||||
} = record;
|
||||
const filepath = OS.Path.join(PERSONALITY_PROVIDER_DIR, filename);
|
||||
|
||||
try {
|
||||
await this.maybeDownloadAttachment(record);
|
||||
return JSON.parse(await this._getFileStr(filepath));
|
||||
} catch (error) {
|
||||
Cu.reportError(`Failed to load ${filepath}: ${error.message}`);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// A helper function to read and decode a file, it isn't a stand alone function.
|
||||
// If you use this, ensure you check the file exists and you have a try catch.
|
||||
async _getFileStr(filepath) {
|
||||
const binaryData = await OS.File.read(filepath);
|
||||
return gTextDecoder.decode(binaryData);
|
||||
}
|
||||
|
||||
async init(callback) {
|
||||
const perfStart = perfService.absNow();
|
||||
this.interestConfig = this.interestConfig || (await this.getRecipe());
|
||||
if (!this.interestConfig) {
|
||||
this.dispatch(
|
||||
ac.PerfEvent({ event: "PERSONALIZATION_V2_GET_RECIPE_ERROR" })
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.recipeExecutor = await this.generateRecipeExecutor();
|
||||
if (!this.recipeExecutor) {
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_GENERATE_RECIPE_EXECUTOR_ERROR",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.interestVector =
|
||||
this.interestVector || (await this.createInterestVector());
|
||||
if (!this.interestVector) {
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_CREATE_INTEREST_VECTOR_ERROR",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_TOTAL_DURATION",
|
||||
value: Math.round(perfService.absNow() - perfStart),
|
||||
})
|
||||
);
|
||||
|
||||
this.initialized = true;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
async getFromRemoteSettings(name) {
|
||||
const result = await RemoteSettings(name).get();
|
||||
return Promise.all(
|
||||
result.map(async record => ({
|
||||
...(await this.getAttachment(record)),
|
||||
recordKey: record.key,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Recipe from remote settings to be consumed by a RecipeExecutor.
|
||||
* A Recipe is a set of instructions on how to processes a RecipeExecutor.
|
||||
*/
|
||||
async getRecipe() {
|
||||
if (!this.recipes || !this.recipes.length) {
|
||||
const start = perfService.absNow();
|
||||
this.recipes = await this.getFromRemoteSettings(RECIPE_NAME);
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_GET_RECIPE_DURATION",
|
||||
value: Math.round(perfService.absNow() - start),
|
||||
})
|
||||
);
|
||||
}
|
||||
return this.recipes[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Recipe Executor.
|
||||
* A Recipe Executor is a set of actions that can be consumed by a Recipe.
|
||||
* The Recipe determines the order and specifics of which the actions are called.
|
||||
*/
|
||||
async generateRecipeExecutor() {
|
||||
if (!this.taggers) {
|
||||
const startTaggers = perfService.absNow();
|
||||
let nbTaggers = [];
|
||||
let nmfTaggers = {};
|
||||
const models = await this.getFromRemoteSettings(MODELS_NAME);
|
||||
|
||||
if (models.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let model of models) {
|
||||
if (!this.modelKeys.includes(model.recordKey)) {
|
||||
continue;
|
||||
}
|
||||
if (model.model_type === "nb") {
|
||||
nbTaggers.push(new NaiveBayesTextTagger(model));
|
||||
} else if (model.model_type === "nmf") {
|
||||
nmfTaggers[model.parent_tag] = new NmfTextTagger(model);
|
||||
}
|
||||
}
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_TAGGERS_DURATION",
|
||||
value: Math.round(perfService.absNow() - startTaggers),
|
||||
})
|
||||
);
|
||||
this.taggers = { nbTaggers, nmfTaggers };
|
||||
}
|
||||
const startRecipeExecutor = perfService.absNow();
|
||||
const recipeExecutor = new RecipeExecutor(
|
||||
this.taggers.nbTaggers,
|
||||
this.taggers.nmfTaggers
|
||||
);
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_RECIPE_EXECUTOR_DURATION",
|
||||
value: Math.round(perfService.absNow() - startRecipeExecutor),
|
||||
})
|
||||
);
|
||||
return recipeExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs a slice of browse history for building a interest vector
|
||||
*/
|
||||
async fetchHistory(columns, beginTimeSecs, endTimeSecs) {
|
||||
let sql = `SELECT url, title, visit_count, frecency, last_visit_date, description
|
||||
FROM moz_places
|
||||
WHERE last_visit_date >= ${beginTimeSecs * 1000000}
|
||||
AND last_visit_date < ${endTimeSecs * 1000000}`;
|
||||
columns.forEach(requiredColumn => {
|
||||
sql += ` AND IFNULL(${requiredColumn}, "") <> ""`;
|
||||
});
|
||||
sql += " LIMIT 30000";
|
||||
|
||||
const { activityStreamProvider } = NewTabUtils;
|
||||
const history = await activityStreamProvider.executePlacesQuery(sql, {
|
||||
columns,
|
||||
params: {},
|
||||
});
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the user's browse history and returns an interest vector that
|
||||
* describes the topics the user frequently browses.
|
||||
*/
|
||||
async createInterestVector() {
|
||||
let interestVector = {};
|
||||
let endTimeSecs = new Date().getTime() / 1000;
|
||||
let beginTimeSecs = endTimeSecs - this.interestConfig.history_limit_secs;
|
||||
let history = await this.fetchHistory(
|
||||
this.interestConfig.history_required_fields,
|
||||
beginTimeSecs,
|
||||
endTimeSecs
|
||||
);
|
||||
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_HISTORY_SIZE",
|
||||
value: history.length,
|
||||
})
|
||||
);
|
||||
|
||||
const start = perfService.absNow();
|
||||
for (let historyRec of history) {
|
||||
let ivItem = this.recipeExecutor.executeRecipe(
|
||||
historyRec,
|
||||
this.interestConfig.history_item_builder
|
||||
);
|
||||
if (ivItem === null) {
|
||||
continue;
|
||||
}
|
||||
interestVector = this.recipeExecutor.executeCombinerRecipe(
|
||||
interestVector,
|
||||
ivItem,
|
||||
this.interestConfig.interest_combiner
|
||||
);
|
||||
if (interestVector === null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const finalResult = this.recipeExecutor.executeRecipe(
|
||||
interestVector,
|
||||
this.interestConfig.interest_finalizer
|
||||
);
|
||||
|
||||
this.dispatch(
|
||||
ac.PerfEvent({
|
||||
event: "PERSONALIZATION_V2_CREATE_INTEREST_VECTOR_DURATION",
|
||||
value: Math.round(perfService.absNow() - start),
|
||||
})
|
||||
);
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a score of a Pocket item when compared to the user's interest
|
||||
* vector. Returns the score. Higher scores are better. Assumes this.interestVector
|
||||
* is populated.
|
||||
*/
|
||||
calculateItemRelevanceScore(pocketItem) {
|
||||
if (!this.initialized) {
|
||||
return pocketItem.item_score || 1;
|
||||
}
|
||||
let scorableItem = this.recipeExecutor.executeRecipe(
|
||||
pocketItem,
|
||||
this.interestConfig.item_to_rank_builder
|
||||
);
|
||||
if (scorableItem === null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let rankingVector = JSON.parse(JSON.stringify(this.interestVector));
|
||||
|
||||
Object.keys(scorableItem).forEach(key => {
|
||||
rankingVector[key] = scorableItem[key];
|
||||
});
|
||||
|
||||
rankingVector = this.recipeExecutor.executeRecipe(
|
||||
rankingVector,
|
||||
this.interestConfig.item_ranker
|
||||
);
|
||||
|
||||
if (rankingVector === null) {
|
||||
return -1;
|
||||
}
|
||||
return rankingVector.score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object holding the settings and affinity scores of this provider instance.
|
||||
*/
|
||||
getAffinities() {
|
||||
return {
|
||||
timeSegments: this.timeSegments,
|
||||
parameterSets: this.parameterSets,
|
||||
maxHistoryQueryResults: this.maxHistoryQueryResults,
|
||||
version: this.version,
|
||||
scores: {
|
||||
interestConfig: this.interestConfig,
|
||||
interestVector: this.interestVector,
|
||||
taggers: this.taggers,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const EXPORTED_SYMBOLS = ["PersonalityProvider"];
|
@ -1,99 +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 { RemoteSettings } = ChromeUtils.import(
|
||||
"resource://services-settings/remote-settings.js"
|
||||
);
|
||||
|
||||
// Returns whether the passed in params match the criteria.
|
||||
// To match, they must contain all the params specified in criteria and the values
|
||||
// must match if a value is provided in criteria.
|
||||
function _hasParams(criteria, params) {
|
||||
for (let param of criteria) {
|
||||
const val = params.get(param.key);
|
||||
if (
|
||||
val === null ||
|
||||
(param.value && param.value !== val) ||
|
||||
(param.prefix && !val.startsWith(param.prefix))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* classifySite
|
||||
* Classifies a given URL into a category based on classification data from RemoteSettings.
|
||||
* The data from remote settings can match a category by one of the following:
|
||||
* - match the exact URL
|
||||
* - match the hostname or second level domain (sld)
|
||||
* - match query parameter(s), and optionally their values or prefixes
|
||||
* - match both (hostname or sld) and query parameter(s)
|
||||
*
|
||||
* The data looks like:
|
||||
* [{
|
||||
* "type": "hostname-and-params-match",
|
||||
* "criteria": [
|
||||
* {
|
||||
* "url": "https://matchurl.com",
|
||||
* "hostname": "matchhostname.com",
|
||||
* "sld": "secondleveldomain",
|
||||
* "params": [
|
||||
* {
|
||||
* "key": "matchparam",
|
||||
* "value": "matchvalue",
|
||||
* "prefix": "matchpPrefix",
|
||||
* },
|
||||
* ],
|
||||
* },
|
||||
* ],
|
||||
* "weight": 300,
|
||||
* },...]
|
||||
*/
|
||||
async function classifySite(url, RS = RemoteSettings) {
|
||||
let category = "other";
|
||||
let parsedURL;
|
||||
|
||||
// Try to parse the url.
|
||||
for (let _url of [url, `https://${url}`]) {
|
||||
try {
|
||||
parsedURL = new URL(_url);
|
||||
break;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (parsedURL) {
|
||||
// If we parsed successfully, find a match.
|
||||
const hostname = parsedURL.hostname.replace(/^www\./i, "");
|
||||
const params = parsedURL.searchParams;
|
||||
// NOTE: there will be an initial/default local copy of the data in m-c.
|
||||
// Therefore, this should never return an empty list [].
|
||||
const siteTypes = await RS("sites-classification").get();
|
||||
const sortedSiteTypes = siteTypes.sort(
|
||||
(x, y) => (y.weight || 0) - (x.weight || 0)
|
||||
);
|
||||
for (let type of sortedSiteTypes) {
|
||||
for (let criteria of type.criteria) {
|
||||
if (criteria.url && criteria.url !== url) {
|
||||
continue;
|
||||
}
|
||||
if (criteria.hostname && criteria.hostname !== hostname) {
|
||||
continue;
|
||||
}
|
||||
if (criteria.sld && criteria.sld !== hostname.split(".")[0]) {
|
||||
continue;
|
||||
}
|
||||
if (criteria.params && !_hasParams(criteria.params, params)) {
|
||||
continue;
|
||||
}
|
||||
return type.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
const EXPORTED_SYMBOLS = ["classifySite"];
|
@ -377,6 +377,10 @@ var PlacesUIUtils = {
|
||||
getViewForNode: function PUIU_getViewForNode(aNode) {
|
||||
let node = aNode;
|
||||
|
||||
if (Cu.isDeadWrapper(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.localName == "panelview" && node._placesView) {
|
||||
return node._placesView;
|
||||
}
|
||||
|
@ -14,11 +14,6 @@ ChromeUtils.defineModuleGetter(
|
||||
"AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"RemoteSettings",
|
||||
"resource://services-settings/remote-settings.js"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"SelectionChangedMenulist",
|
||||
@ -57,16 +52,6 @@ async function installFromUrl(url, hash, callback) {
|
||||
return install.addon;
|
||||
}
|
||||
|
||||
async function dictionaryIdsForLocale(locale) {
|
||||
let entries = await RemoteSettings("language-dictionaries").get({
|
||||
filters: { id: locale },
|
||||
});
|
||||
if (entries.length > 0) {
|
||||
return entries[0].dictionaries;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
class OrderedListBox {
|
||||
constructor({
|
||||
richlistbox,
|
||||
|
@ -15,6 +15,6 @@
|
||||
}
|
||||
|
||||
.option-description {
|
||||
color: #737373;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
margin-top: -0.5em !important;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
}
|
||||
|
||||
#permissionsDisableDescription {
|
||||
color: #737373;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
line-height: 110%;
|
||||
}
|
||||
|
||||
|
@ -147,11 +147,9 @@ add_task(async function test_search_handoff_on_paste() {
|
||||
await new Promise(r =>
|
||||
EventUtils.synthesizeKey("v", { accelKey: true }, win, r)
|
||||
);
|
||||
// TODO: Bug 1539199 We should be able to wait for search complete for AwesomeBar
|
||||
// as well.
|
||||
if (UrlbarPrefs.get("quantumbar")) {
|
||||
await UrlbarTestUtils.promiseSearchComplete(win);
|
||||
}
|
||||
|
||||
await UrlbarTestUtils.promiseSearchComplete(win);
|
||||
|
||||
ok(urlBarHasNormalFocus(win), "url bar has normal focused");
|
||||
is(win.gURLBar.value, "@google words", "url bar has search text");
|
||||
|
||||
|
@ -714,17 +714,6 @@ class SearchOneOffs {
|
||||
}
|
||||
|
||||
_buttonForEngine(engine) {
|
||||
if (this.telemetryOrigin == "urlbar" && !UrlbarPrefs.get("quantumbar")) {
|
||||
return (
|
||||
this._popup &&
|
||||
document.getAnonymousElementByAttribute(
|
||||
this._popup,
|
||||
"id",
|
||||
this._buttonIDForEngine(engine)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let id = this._buttonIDForEngine(engine);
|
||||
return this._popup && document.getElementById(id);
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ const ONEOFF_URLBAR_PREF = "browser.urlbar.oneOffSearches";
|
||||
|
||||
const urlbar = document.getElementById("urlbar");
|
||||
const searchPopup = document.getElementById("PopupSearchAutoComplete");
|
||||
const urlbarPopup = document.getElementById("PopupAutoCompleteRichResult");
|
||||
const searchOneOff = searchPopup.oneOffButtons;
|
||||
const urlBarOneOff = UrlbarTestUtils.getOneOffSearchButtons(window);
|
||||
|
||||
@ -88,7 +87,7 @@ add_task(async function test_urlBarChangeEngine() {
|
||||
|
||||
let oneOffButton = await openPopupAndGetEngineButton(
|
||||
false,
|
||||
urlbarPopup,
|
||||
null,
|
||||
urlBarOneOff,
|
||||
URLBAR_BASE_ID
|
||||
);
|
||||
@ -126,7 +125,7 @@ add_task(async function test_urlBarChangeEngine() {
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
|
||||
// Move the cursor out of the panel area to avoid messing with other tests.
|
||||
await EventUtils.synthesizeNativeMouseMove(urlbarPopup);
|
||||
await EventUtils.synthesizeNativeMouseMove(urlbar);
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -111,7 +111,6 @@ add_task(async function test_unrestored_tabs_listed() {
|
||||
});
|
||||
const total = UrlbarTestUtils.getResultCount(window);
|
||||
info(`Found ${total} matches`);
|
||||
const quantumbar = UrlbarPrefs.get("quantumbar");
|
||||
|
||||
// Check to see the expected uris and titles match up (in any order)
|
||||
for (let i = 0; i < total; i++) {
|
||||
@ -120,9 +119,7 @@ add_task(async function test_unrestored_tabs_listed() {
|
||||
info("Skip heuristic match");
|
||||
continue;
|
||||
}
|
||||
const url = quantumbar
|
||||
? result.url
|
||||
: PlacesUtils.parseActionUrl(result.url).params.url;
|
||||
const url = result.url;
|
||||
Assert.ok(
|
||||
url in tabsForEnsure,
|
||||
`Should have the found result '${url}' in the expected list of entries`
|
||||
|
@ -150,7 +150,8 @@ nsMacShellService::SetDesktopBackground(Element* aElement, int32_t aPosition,
|
||||
|
||||
auto referrerInfo = mozilla::MakeRefPtr<mozilla::dom::ReferrerInfo>(*aElement);
|
||||
return wbp->SaveURI(imageURI, aElement->NodePrincipal(), 0, referrerInfo,
|
||||
nullptr, nullptr, mBackgroundFile, loadContext);
|
||||
nullptr, nullptr, mBackgroundFile,
|
||||
nsIContentPolicy::TYPE_IMAGE, loadContext);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -97,7 +97,6 @@ class UrlbarInput {
|
||||
this.controller.setInput(this);
|
||||
this.view = new UrlbarView(this);
|
||||
this.valueIsTyped = false;
|
||||
this.userInitiatedFocus = false;
|
||||
this.isPrivate = PrivateBrowsingUtils.isWindowPrivate(this.window);
|
||||
this.lastQueryContextPromise = Promise.resolve();
|
||||
this._actionOverrideKeyCount = 0;
|
||||
@ -176,28 +175,32 @@ class UrlbarInput {
|
||||
this.eventBufferer = new UrlbarEventBufferer(this);
|
||||
|
||||
this._inputFieldEvents = [
|
||||
"blur",
|
||||
"compositionstart",
|
||||
"compositionend",
|
||||
"dragover",
|
||||
"dragstart",
|
||||
"drop",
|
||||
"focus",
|
||||
"blur",
|
||||
"input",
|
||||
"keydown",
|
||||
"keyup",
|
||||
"mousedown",
|
||||
"mouseover",
|
||||
"overflow",
|
||||
"underflow",
|
||||
"paste",
|
||||
"scrollend",
|
||||
"select",
|
||||
"overflow",
|
||||
"underflow",
|
||||
"dragstart",
|
||||
"dragover",
|
||||
"drop",
|
||||
"compositionstart",
|
||||
"compositionend",
|
||||
];
|
||||
for (let name of this._inputFieldEvents) {
|
||||
this.inputField.addEventListener(name, this);
|
||||
}
|
||||
|
||||
// This is needed for the dropmarker. Once we remove that (i.e. make
|
||||
// openViewOnFocus = true the default), this won't be needed anymore.
|
||||
this.addEventListener("mousedown", this);
|
||||
|
||||
this.view.panel.addEventListener("popupshowing", this);
|
||||
this.view.panel.addEventListener("popuphidden", this);
|
||||
|
||||
@ -291,15 +294,6 @@ class UrlbarInput {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This exists for legacy compatibility, and can be removed once the old
|
||||
* urlbar code goes away, by changing callers. Internal consumers should use
|
||||
* view.close().
|
||||
*/
|
||||
closePopup() {
|
||||
this.view.close();
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.inputField.focus();
|
||||
}
|
||||
@ -1448,10 +1442,6 @@ class UrlbarInput {
|
||||
if (this.getAttribute("pageproxystate") != "valid") {
|
||||
this.window.UpdatePopupNotificationsVisibility();
|
||||
}
|
||||
|
||||
if (this.openViewOnFocus) {
|
||||
this.startQuery();
|
||||
}
|
||||
}
|
||||
|
||||
_on_mouseover(event) {
|
||||
@ -1459,23 +1449,23 @@ class UrlbarInput {
|
||||
}
|
||||
|
||||
_on_mousedown(event) {
|
||||
if (
|
||||
(event.target == this.inputField ||
|
||||
// Can be removed after bug 1513337:
|
||||
event.originalTarget.classList.contains("anonymous-div")) &&
|
||||
event.button == 0 &&
|
||||
event.detail == 2 &&
|
||||
UrlbarPrefs.get("doubleClickSelectsAll")
|
||||
) {
|
||||
this.editor.selectAll();
|
||||
event.preventDefault();
|
||||
// We only care about left clicks here.
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
event.originalTarget.classList.contains("urlbar-history-dropmarker") &&
|
||||
event.button == 0
|
||||
) {
|
||||
if (event.currentTarget == this.inputField) {
|
||||
if (event.detail == 2 &&
|
||||
UrlbarPrefs.get("doubleClickSelectsAll")) {
|
||||
this.editor.selectAll();
|
||||
event.preventDefault();
|
||||
} else if (this.openViewOnFocus && !this.view.isOpen) {
|
||||
this.startQuery();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.originalTarget.classList.contains("urlbar-history-dropmarker")) {
|
||||
if (this.view.isOpen) {
|
||||
this.view.close();
|
||||
} else {
|
||||
@ -1631,9 +1621,6 @@ class UrlbarInput {
|
||||
_on_TabSelect(event) {
|
||||
this._resetSearchState();
|
||||
this.controller.viewContextChanged();
|
||||
if (this.focused && this.openViewOnFocus) {
|
||||
this.startQuery();
|
||||
}
|
||||
}
|
||||
|
||||
_on_keydown(event) {
|
||||
|
@ -341,9 +341,7 @@ class UrlbarValueFormatter {
|
||||
return false;
|
||||
}
|
||||
|
||||
let alias = UrlbarPrefs.get("quantumbar")
|
||||
? this._getSearchAlias()
|
||||
: this._getSearchAliasAwesomebar();
|
||||
let alias = this._getSearchAlias();
|
||||
if (!alias) {
|
||||
return false;
|
||||
}
|
||||
@ -419,42 +417,6 @@ class UrlbarValueFormatter {
|
||||
return null;
|
||||
}
|
||||
|
||||
_getSearchAliasAwesomebar() {
|
||||
let popup = this.urlbarInput.popup;
|
||||
|
||||
// To determine whether the input contains a valid alias, check the value of
|
||||
// the selected result -- whether it's a search engine result with an alias.
|
||||
// Actually, check the selected listbox item, not the result in the
|
||||
// controller, because we want to continue highlighting the alias when the
|
||||
// popup is closed and the search has stopped. The selected index when the
|
||||
// popup is closed is zero, however, which is why we also check the previous
|
||||
// selected index.
|
||||
let itemIndex =
|
||||
popup.selectedIndex < 0
|
||||
? popup._previousSelectedIndex
|
||||
: popup.selectedIndex;
|
||||
if (itemIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
let item = popup.richlistbox.children[itemIndex] || null;
|
||||
|
||||
// This actiontype check isn't necessary because we call _parseActionUrl
|
||||
// below and we could check action.type instead. But since this method is
|
||||
// called very often, as an optimization, first do a simple string
|
||||
// comparison on actiontype before continuing with the more expensive regexp
|
||||
// that _parseActionUrl uses.
|
||||
if (!item || item.getAttribute("actiontype") != "searchengine") {
|
||||
return null;
|
||||
}
|
||||
|
||||
let url = item.getAttribute("url");
|
||||
let action = this.urlbarInput._parseActionUrl(url);
|
||||
if (!action) {
|
||||
return null;
|
||||
}
|
||||
return action.params.alias || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes DOM events to the _on_<event type> methods.
|
||||
* @param {Event} event
|
||||
|
9
browser/components/urlbar/docs/contact.rst
Normal file
9
browser/components/urlbar/docs/contact.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Getting in Touch
|
||||
================
|
||||
|
||||
For any questions regarding the Address Bar, the team is available through
|
||||
the #fx-search channel on irc.mozilla.org and the fx-search@mozilla.com mailing
|
||||
list.
|
||||
|
||||
Issues can be `filed in Bugzilla <https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Address%20Bar>`_
|
||||
under the Firefox / Address Bar component.
|
4
browser/components/urlbar/docs/debugging.rst
Normal file
4
browser/components/urlbar/docs/debugging.rst
Normal file
@ -0,0 +1,4 @@
|
||||
Debugging & Logging
|
||||
===================
|
||||
|
||||
*Content to be written*
|
4
browser/components/urlbar/docs/experiments.rst
Normal file
4
browser/components/urlbar/docs/experiments.rst
Normal file
@ -0,0 +1,4 @@
|
||||
Experiments & Extensions
|
||||
========================
|
||||
|
||||
*Content to be written*
|
26
browser/components/urlbar/docs/index.rst
Normal file
26
browser/components/urlbar/docs/index.rst
Normal file
@ -0,0 +1,26 @@
|
||||
Address Bar
|
||||
===========
|
||||
|
||||
This document describes the implementation of Firefox's address bar, also known
|
||||
as the quantumbar or urlbar. The address bar was also called the awesomebar
|
||||
until Firefox 68, when it was substantially rewritten.
|
||||
|
||||
The address bar is a specialized search access point that aggregates data from
|
||||
several different sources, including:
|
||||
|
||||
* Places (Firefox's history and bookmarks system)
|
||||
* Search engines (including search suggestions)
|
||||
* WebExtensions
|
||||
* Open tabs
|
||||
|
||||
Most of the address bar code lives in `browser/components/urlbar <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/>`_.
|
||||
A separate and important back-end piece currently is `toolkit/components/places/UnifiedComplete.jsm <https://dxr.mozilla.org/mozilla-central/source/toolkit/components/places/UnifiedComplete.jsm>`_, which was carried over from awesomebar and has not yet been rewritten for quantumbar.
|
||||
|
||||
.. toctree::
|
||||
|
||||
overview
|
||||
utilities
|
||||
telemetry
|
||||
debugging
|
||||
experiments
|
||||
contact
|
@ -1,36 +1,7 @@
|
||||
.. _addressbar:
|
||||
Architecture Overview
|
||||
=====================
|
||||
|
||||
===========
|
||||
Address Bar
|
||||
===========
|
||||
|
||||
The *Address Bar* component drives the browser's url bar, a specialized search
|
||||
access point (SAP) including different sources of information:
|
||||
* Places: the History, Bookmarks, Favicons and Tags component
|
||||
* Search Engines and Suggestions
|
||||
* WebExtensions
|
||||
* Open Tabs
|
||||
* Keywords and aliases
|
||||
|
||||
The *Address Bar* component lives in the
|
||||
`browser/components/urlbar/ <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/>`_ folder.
|
||||
|
||||
.. note::
|
||||
|
||||
The current *Address Bar* is driven by the legacy
|
||||
`toolkit autocomplete <https://dxr.mozilla.org/mozilla-central/source/toolkit/components/autocomplete>`_
|
||||
binding, along with the *Places UnifiedComplete* component. This documentation
|
||||
describes a new rewrite of the feature, also known as the **Quantum Bar**.
|
||||
|
||||
.. caution::
|
||||
|
||||
This code is under heavy development and may change abruptly.
|
||||
|
||||
|
||||
Global Architecture Overview
|
||||
============================
|
||||
|
||||
The *Address Bar* is implemented as a *Model-View-Controller* (MVC) system. One of
|
||||
The address bar is implemented as a *model-view-controller* (MVC) system. One of
|
||||
the scopes of this architecture is to allow easy replacement of its components,
|
||||
for easier experimentation.
|
||||
|
||||
@ -46,7 +17,7 @@ and responsibilities.
|
||||
|
||||
|
||||
The UrlbarQueryContext
|
||||
======================
|
||||
----------------------
|
||||
|
||||
The *UrlbarQueryContext* object describes a single instance of a search.
|
||||
It is augmented as it progresses through the system, with various information:
|
||||
@ -85,7 +56,7 @@ It is augmented as it progresses through the system, with various information:
|
||||
|
||||
|
||||
The Model
|
||||
=========
|
||||
---------
|
||||
|
||||
The *Model* is the component responsible for retrieving search results based on
|
||||
the user's input, and sorting them accordingly to their importance.
|
||||
@ -134,7 +105,7 @@ used by the user to restrict the search to specific result type (See the
|
||||
}
|
||||
|
||||
UrlbarProvider
|
||||
--------------
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
A provider is specialized into searching and returning results from different
|
||||
information sources. Internal providers are usually implemented in separate
|
||||
@ -218,7 +189,7 @@ implementation details may vary deeply among different providers.
|
||||
}
|
||||
|
||||
UrlbarMuxer
|
||||
-----------
|
||||
~~~~~~~~~~~
|
||||
|
||||
The *Muxer* is responsible for sorting results based on their importance and
|
||||
additional rules that depend on the UrlbarQueryContext. The muxer to use is
|
||||
@ -253,7 +224,7 @@ indicated by the UrlbarQueryContext.muxer property.
|
||||
|
||||
|
||||
The Controller
|
||||
==============
|
||||
--------------
|
||||
|
||||
`UrlbarController <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/UrlbarController.jsm>`_
|
||||
is the component responsible for reacting to user's input, by communicating
|
||||
@ -282,7 +253,7 @@ View (e.g. showing/hiding a panel). It is also responsible for reporting Telemet
|
||||
|
||||
|
||||
The View
|
||||
=========
|
||||
--------
|
||||
|
||||
The View is the component responsible for presenting search results to the
|
||||
user and handling their input.
|
||||
@ -293,7 +264,7 @@ user and handling their input.
|
||||
reference for the default View, but may not be valid for other implementations.
|
||||
|
||||
`UrlbarInput.jsm <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/UrlbarInput.jsm>`_
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Implements an input box *View*, owns an *UrlbarView*.
|
||||
|
||||
@ -307,8 +278,6 @@ Implements an input box *View*, owns an *UrlbarView*.
|
||||
// Uses UrlbarValueFormatter to highlight the base host, search aliases
|
||||
// and to keep the host visible on overflow.
|
||||
formatValue(val);
|
||||
// Manage view visibility.
|
||||
closePopup();
|
||||
openResults();
|
||||
// Converts an internal URI (e.g. a URI with a username or password) into
|
||||
// one which we can expose to the user.
|
||||
@ -331,8 +300,6 @@ Implements an input box *View*, owns an *UrlbarView*.
|
||||
view;
|
||||
// Whether the current value was typed by the user.
|
||||
valueIsTyped;
|
||||
// Whether the input box has been focused by a user action.
|
||||
userInitiatedFocus;
|
||||
// Whether the context is in Private Browsing mode.
|
||||
isPrivate;
|
||||
// Whether the input box is focused.
|
||||
@ -344,7 +311,7 @@ Implements an input box *View*, owns an *UrlbarView*.
|
||||
}
|
||||
|
||||
`UrlbarView.jsm <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/UrlbarView.jsm>`_
|
||||
-----------------------------------------------------------------------------------------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Represents the base *View* implementation, communicates with the *Controller*.
|
||||
|
||||
@ -371,7 +338,7 @@ Represents the base *View* implementation, communicates with the *Controller*.
|
||||
|
||||
|
||||
UrlbarResult
|
||||
============
|
||||
------------
|
||||
|
||||
An `UrlbarResult <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/UrlbarResult.jsm>`_
|
||||
instance represents a single search result with a result type, that
|
||||
@ -419,54 +386,3 @@ The following RESULT_TYPEs are supported:
|
||||
OMNIBOX: 5,
|
||||
// Payload: { icon, url, device, title }
|
||||
REMOTE_TAB: 6,
|
||||
|
||||
|
||||
Shared Modules
|
||||
==============
|
||||
|
||||
Various modules provide shared utilities to the other components:
|
||||
|
||||
`UrlbarPrefs.jsm <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/UrlbarPrefs.jsm>`_
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Implements a Map-like storage or urlbar related preferences. The values are kept
|
||||
up-to-date.
|
||||
|
||||
.. highlight:: JavaScript
|
||||
.. code::
|
||||
|
||||
// Always use browser.urlbar. relative branch, except for the preferences in
|
||||
// PREF_OTHER_DEFAULTS.
|
||||
UrlbarPrefs.get("delay"); // Gets value of browser.urlbar.delay.
|
||||
|
||||
.. note::
|
||||
|
||||
Newly added preferences should always be properly documented in UrlbarPrefs.
|
||||
|
||||
`UrlbarUtils.jsm <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/UrlbarUtils.jsm>`_
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Includes shared utils and constants shared across all the components.
|
||||
|
||||
|
||||
Telemetry Probes
|
||||
================
|
||||
|
||||
*Content to be written*
|
||||
|
||||
|
||||
Debugging & Logging
|
||||
===================
|
||||
|
||||
*Content to be written*
|
||||
|
||||
|
||||
Getting in Touch
|
||||
================
|
||||
|
||||
For any questions regarding the Address Bar, the team is available through
|
||||
the #fx-search channel on irc.mozilla.org and the fx-search@mozilla.com mailing
|
||||
list.
|
||||
|
||||
Issues can be `filed in Bugzilla <https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Address%20Bar>`_
|
||||
under the Firefox / Address Bar component.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user