Update On Wed Jul 17 20:48:28 CEST 2024

This commit is contained in:
github-action[bot] 2024-07-17 20:48:29 +02:00
parent 776d6ead9e
commit 1239d9ad40
1160 changed files with 48589 additions and 115871 deletions

90
Cargo.lock generated
View file

@ -44,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce34de545ad29bcc00cb1b87a94c132256dcf83aa7eeb9674482568405a6ff0a"
dependencies = [
"alsa-sys",
"bitflags 2.5.0",
"bitflags 2.6.0",
"libc",
"nix 0.26.99",
]
@ -444,7 +444,7 @@ dependencies = [
name = "bindgen"
version = "0.69.4"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cexpr",
"clang-sys",
"itertools",
@ -460,31 +460,31 @@ dependencies = [
[[package]]
name = "bit-set"
version = "0.5.3"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
[[package]]
name = "bitflags"
version = "1.999.999"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
dependencies = [
"serde",
]
@ -1028,6 +1028,7 @@ dependencies = [
"env_logger",
"flate2",
"fluent",
"glean",
"gtkbind",
"intl-memoizer",
"libloading",
@ -1203,7 +1204,7 @@ source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=8bce3b333a920999
dependencies = [
"atomic",
"audio-mixer",
"bitflags 2.5.0",
"bitflags 2.6.0",
"coreaudio-sys-utils",
"cubeb-backend",
"float-cmp",
@ -1239,9 +1240,9 @@ dependencies = [
[[package]]
name = "d3d12"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a#82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0c185a28c232ee2ab63f72d6fd3a63a3f787309#a0c185a28c232ee2ab63f72d6fd3a63a3f787309"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"libloading",
"winapi",
]
@ -1545,7 +1546,7 @@ dependencies = [
name = "dom"
version = "0.1.0"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
@ -2535,7 +2536,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"gpu-alloc-types",
]
@ -2545,7 +2546,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
@ -2566,7 +2567,7 @@ name = "gpu-descriptor"
version = "0.3.0"
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=7b71a4e47c81903ad75e2c53deb5ab1310f6ff4d#7b71a4e47c81903ad75e2c53deb5ab1310f6ff4d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"gpu-descriptor-types",
"hashbrown 0.14.5",
]
@ -2576,7 +2577,7 @@ name = "gpu-descriptor-types"
version = "0.2.0"
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=7b71a4e47c81903ad75e2c53deb5ab1310f6ff4d#7b71a4e47c81903ad75e2c53deb5ab1310f6ff4d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
@ -3663,7 +3664,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"block",
"core-graphics-types",
"foreign-types",
@ -3728,7 +3729,7 @@ version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bb6eaf88cc770fa58e6ae721cf2e40c2ca6a4c942ae8c7aa324d680bd3c6717"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"debugid",
"num-derive",
"num-traits",
@ -3743,7 +3744,7 @@ version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abcd9c8a1e6e1e9d56ce3627851f39a17ea83e17c96bc510f29d7e43d78a7d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"cfg-if",
"crash-context",
@ -3872,7 +3873,7 @@ version = "0.1.0"
dependencies = [
"arrayvec",
"bindgen 0.69.4",
"bitflags 2.5.0",
"bitflags 2.6.0",
"bytes",
"cc",
"chrono",
@ -4031,11 +4032,12 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
[[package]]
name = "naga"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a#82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0c185a28c232ee2ab63f72d6fd3a63a3f787309#a0c185a28c232ee2ab63f72d6fd3a63a3f787309"
dependencies = [
"arrayvec",
"bit-set",
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg_aliases",
"codespan-reporting",
"hexf-parse",
"indexmap 2.2.6",
@ -4191,7 +4193,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg-if",
"cfg_aliases",
"libc",
@ -4252,7 +4254,7 @@ source = "git+https://github.com/mozilla/application-services?rev=8fd08c6f2f8acd
name = "nsstring"
version = "0.1.0"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"encoding_rs",
]
@ -4702,7 +4704,7 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"hex",
]
@ -4759,7 +4761,7 @@ name = "pulse"
version = "0.3.0"
source = "git+https://github.com/mozilla/cubeb-pulse-rs?rev=8678dcab1c287de79c4c184ccc2e065bc62b70e2#8678dcab1c287de79c4c184ccc2e065bc62b70e2"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"pulse-ffi",
]
@ -4997,7 +4999,7 @@ checksum = "2c6d906922d99c677624d2042a93f89b2b7df0f6411032237d5d99a602c2487c"
dependencies = [
"arrayref",
"bincode",
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"id-arena",
"lazy_static",
@ -5019,7 +5021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [
"base64 0.21.3",
"bitflags 2.5.0",
"bitflags 2.6.0",
"serde",
"serde_derive",
]
@ -5062,7 +5064,7 @@ version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
@ -5136,7 +5138,7 @@ version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@ -5200,7 +5202,7 @@ dependencies = [
name = "selectors"
version = "0.22.0"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cssparser",
"derive_more 0.99.999",
"fxhash",
@ -5483,7 +5485,7 @@ version = "0.3.0+sdk-1.3.268.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
@ -5573,7 +5575,7 @@ dependencies = [
"arrayvec",
"atomic_refcell",
"bindgen 0.69.4",
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"cssparser",
"derive_more 0.99.999",
@ -5639,7 +5641,7 @@ name = "style_traits"
version = "0.0.1"
dependencies = [
"app_units",
"bitflags 2.5.0",
"bitflags 2.6.0",
"cssparser",
"euclid",
"lazy_static",
@ -6657,7 +6659,7 @@ name = "webrender"
version = "0.62.0"
dependencies = [
"bincode",
"bitflags 2.5.0",
"bitflags 2.6.0",
"build-parallel",
"byteorder",
"derive_more 0.99.999",
@ -6694,7 +6696,7 @@ name = "webrender_api"
version = "0.62.0"
dependencies = [
"app_units",
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"crossbeam-channel",
"euclid",
@ -6741,7 +6743,7 @@ dependencies = [
name = "webrender_build"
version = "0.0.2"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"lazy_static",
"serde",
]
@ -6768,11 +6770,11 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a#82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0c185a28c232ee2ab63f72d6fd3a63a3f787309#a0c185a28c232ee2ab63f72d6fd3a63a3f787309"
dependencies = [
"arrayvec",
"bit-vec",
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg_aliases",
"document-features",
"indexmap 2.2.6",
@ -6793,13 +6795,13 @@ dependencies = [
[[package]]
name = "wgpu-hal"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a#82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0c185a28c232ee2ab63f72d6fd3a63a3f787309#a0c185a28c232ee2ab63f72d6fd3a63a3f787309"
dependencies = [
"android_system_properties",
"arrayvec",
"ash",
"bit-set",
"bitflags 2.5.0",
"bitflags 2.6.0",
"block",
"cfg_aliases",
"core-graphics-types",
@ -6832,9 +6834,9 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a#82210e1cdc63cbd5e53f43788f9956bb0d4a2c6a"
source = "git+https://github.com/gfx-rs/wgpu?rev=a0c185a28c232ee2ab63f72d6fd3a63a3f787309#a0c185a28c232ee2ab63f72d6fd3a63a3f787309"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"js-sys",
"serde",
"web-sys",

View file

@ -36,6 +36,11 @@ source-repo.h: $(MDDEPDIR)/source-repo.h.stub
buildid.h: $(MDDEPDIR)/buildid.h.stub
# Add explicit dependencies that moz.build can't declare yet.
build/$(MDDEPDIR)/application.ini.stub: source-repo.h buildid.h
# The mozbuild crate includes the buildid (via `variables.py:get_buildid()`),
# so it can only be generated after the buildid file is generated.
ifeq ($(and $(JS_STANDALONE),$(MOZ_BUILD_APP)),)
build/rust/mozbuild/$(MDDEPDIR)/buildconfig.rs.stub: buildid.h
endif
BUILD_BACKEND_FILES := $(addprefix backend.,$(addsuffix Backend,$(BUILD_BACKENDS)))

View file

@ -53,10 +53,11 @@ struct AtkStateMap {
};
// Map array from cross platform states to ATK states
static const AtkStateMap gAtkStateMap[] =
{
// Cross Platform States
// clang-format off
static const AtkStateMap
gAtkStateMap[] =
{
// Cross Platform States
// clang-format off
{ kNone, kMapOpposite }, // states::UNAVAILABLE = 1 << 0
{ ATK_STATE_SELECTED, kMapDirectly }, // states::SELECTED = 1 << 1
{ ATK_STATE_FOCUSED, kMapDirectly }, // states::FOCUSED = 1 << 2
@ -106,7 +107,7 @@ static const AtkStateMap gAtkStateMap[] =
{ ATK_STATE_EXPANDABLE, kMapDirectly }, // states::EXPANDABLE = 1 << 46
{ kNone, kMapDirectly }, // states::PINNED = 1 << 47
{ ATK_STATE_ACTIVE, kMapDirectly } // states::CURRENT = 1 << 48
// clang-format on
// clang-format on
};
static const auto gAtkStateMapLen = std::extent<decltype(gAtkStateMap)>::value;

View file

@ -231,7 +231,7 @@ void EventQueue::CoalesceEvents() {
default:
break; // case eAllowDupes, eDoNotEmit
} // switch
} // switch
}
void EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,

View file

@ -211,7 +211,7 @@ class TextAttrsMgr {
class InvalidTextAttr : public TTextAttr<uint32_t> {
public:
InvalidTextAttr(nsIContent* aRootElm, nsIContent* aElm);
virtual ~InvalidTextAttr(){};
virtual ~InvalidTextAttr() {};
protected:
enum { eFalse, eGrammar, eSpelling, eTrue };

View file

@ -372,14 +372,13 @@ static int32_t sPlatformDisabledState = 0;
////////////////////////////////////////////////////////////////////////////////
// Markup maps array.
#define Attr(name, value) \
{ nsGkAtoms::name, nsGkAtoms::value }
#define Attr(name, value) {nsGkAtoms::name, nsGkAtoms::value}
#define AttrFromDOM(name, DOMAttrName) \
{ nsGkAtoms::name, nullptr, nsGkAtoms::DOMAttrName }
{nsGkAtoms::name, nullptr, nsGkAtoms::DOMAttrName}
#define AttrFromDOMIf(name, DOMAttrName, DOMAttrValue) \
{ nsGkAtoms::name, nullptr, nsGkAtoms::DOMAttrName, nsGkAtoms::DOMAttrValue }
{nsGkAtoms::name, nullptr, nsGkAtoms::DOMAttrName, nsGkAtoms::DOMAttrValue}
#define MARKUPMAP(atom, new_func, r, ...) \
{nsGkAtoms::atom, new_func, static_cast<a11y::role>(r), {__VA_ARGS__}},

View file

@ -77,10 +77,10 @@ uint16_t RotorRule::Match(Accessible* aAcc) {
RotorRoleRule::RotorRoleRule(role aRole, Accessible* aDirectDescendantsFrom,
const nsString& aSearchText)
: RotorRule(aDirectDescendantsFrom, aSearchText), mRole(aRole){};
: RotorRule(aDirectDescendantsFrom, aSearchText), mRole(aRole) {};
RotorRoleRule::RotorRoleRule(role aRole, const nsString& aSearchText)
: RotorRule(aSearchText), mRole(aRole){};
: RotorRule(aSearchText), mRole(aRole) {};
uint16_t RotorRoleRule::Match(Accessible* aAcc) {
uint16_t result = RotorRule::Match(aAcc);
@ -135,10 +135,10 @@ uint16_t RotorMacRoleRule::Match(Accessible* aAcc) {
RotorControlRule::RotorControlRule(Accessible* aDirectDescendantsFrom,
const nsString& aSearchText)
: RotorRule(aDirectDescendantsFrom, aSearchText){};
: RotorRule(aDirectDescendantsFrom, aSearchText) {};
RotorControlRule::RotorControlRule(const nsString& aSearchText)
: RotorRule(aSearchText){};
: RotorRule(aSearchText) {};
uint16_t RotorControlRule::Match(Accessible* aAcc) {
uint16_t result = RotorRule::Match(aAcc);
@ -207,10 +207,10 @@ uint16_t RotorControlRule::Match(Accessible* aAcc) {
RotorTextEntryRule::RotorTextEntryRule(Accessible* aDirectDescendantsFrom,
const nsString& aSearchText)
: RotorRule(aDirectDescendantsFrom, aSearchText){};
: RotorRule(aDirectDescendantsFrom, aSearchText) {};
RotorTextEntryRule::RotorTextEntryRule(const nsString& aSearchText)
: RotorRule(aSearchText){};
: RotorRule(aSearchText) {};
uint16_t RotorTextEntryRule::Match(Accessible* aAcc) {
uint16_t result = RotorRule::Match(aAcc);
@ -232,10 +232,10 @@ uint16_t RotorTextEntryRule::Match(Accessible* aAcc) {
RotorLinkRule::RotorLinkRule(Accessible* aDirectDescendantsFrom,
const nsString& aSearchText)
: RotorRule(aDirectDescendantsFrom, aSearchText){};
: RotorRule(aDirectDescendantsFrom, aSearchText) {};
RotorLinkRule::RotorLinkRule(const nsString& aSearchText)
: RotorRule(aSearchText){};
: RotorRule(aSearchText) {};
uint16_t RotorLinkRule::Match(Accessible* aAcc) {
uint16_t result = RotorRule::Match(aAcc);
@ -325,10 +325,10 @@ uint16_t RotorNotMacRoleRule::Match(Accessible* aAcc) {
RotorStaticTextRule::RotorStaticTextRule(Accessible* aDirectDescendantsFrom,
const nsString& aSearchText)
: RotorRule(aDirectDescendantsFrom, aSearchText){};
: RotorRule(aDirectDescendantsFrom, aSearchText) {};
RotorStaticTextRule::RotorStaticTextRule(const nsString& aSearchText)
: RotorRule(aSearchText){};
: RotorRule(aSearchText) {};
uint16_t RotorStaticTextRule::Match(Accessible* aAcc) {
uint16_t result = RotorRule::Match(aAcc);
@ -353,11 +353,11 @@ RotorHeadingLevelRule::RotorHeadingLevelRule(int32_t aLevel,
Accessible* aDirectDescendantsFrom,
const nsString& aSearchText)
: RotorRoleRule(roles::HEADING, aDirectDescendantsFrom, aSearchText),
mLevel(aLevel){};
mLevel(aLevel) {};
RotorHeadingLevelRule::RotorHeadingLevelRule(int32_t aLevel,
const nsString& aSearchText)
: RotorRoleRule(roles::HEADING, aSearchText), mLevel(aLevel){};
: RotorRoleRule(roles::HEADING, aSearchText), mLevel(aLevel) {};
uint16_t RotorHeadingLevelRule::Match(Accessible* aAcc) {
uint16_t result = RotorRoleRule::Match(aAcc);

View file

@ -17,8 +17,8 @@ namespace a11y {
class sdnDocAccessible final : public ISimpleDOMDocument {
public:
explicit sdnDocAccessible(MsaaDocAccessible* aMsaa) : mMsaa(aMsaa){};
~sdnDocAccessible(){};
explicit sdnDocAccessible(MsaaDocAccessible* aMsaa) : mMsaa(aMsaa) {};
~sdnDocAccessible() {};
DECL_IUNKNOWN

View file

@ -20,7 +20,7 @@ namespace a11y {
class sdnTextAccessible final : public ISimpleDOMText {
public:
explicit sdnTextAccessible(MsaaAccessible* aMsaa) : mMsaa(aMsaa){};
explicit sdnTextAccessible(MsaaAccessible* aMsaa) : mMsaa(aMsaa) {};
~sdnTextAccessible() {}
DECL_IUNKNOWN

View file

@ -103,8 +103,8 @@ uint64_t XULMenuitemAccessible::NativeState() const {
(grandParentState & states::INVISIBLE) |
(grandParentState & states::OPAQUE1);
} // isCollapsed
} // isSelected
} // ROLE_COMBOBOX_OPTION
} // isSelected
} // ROLE_COMBOBOX_OPTION
return state;
}

View file

@ -889,6 +889,7 @@ class SearchAdImpression {
opacityProperty: true,
})
) {
Glean.serp.adsBlockedCount.hidden_parent.add();
return {
adsVisible: 0,
adsHidden: adsLoaded,
@ -901,6 +902,7 @@ class SearchAdImpression {
elementRect.bottom < 0 &&
innerWindowHeight + scrollY + elementRect.bottom < 0
) {
Glean.serp.adsBlockedCount.beyond_viewport.add();
return {
adsVisible: 0,
adsHidden: adsLoaded,
@ -933,6 +935,7 @@ class SearchAdImpression {
})
) {
adsHidden += 1;
Glean.serp.adsBlockedCount.hidden_child.add();
continue;
}

View file

@ -668,10 +668,6 @@ pref("browser.urlbar.addons.minKeywordLength", 0);
// Feature gate pref for Pocket suggestions in the urlbar.
pref("browser.urlbar.pocket.featureGate", false);
// The group-relative suggestedIndex of Pocket suggestions within the Firefox
// Suggest section.
pref("browser.urlbar.pocket.suggestedIndex", 0);
// If `browser.urlbar.pocket.featureGate` is true, this controls whether Pocket
// suggestions are turned on.
pref("browser.urlbar.suggest.pocket", true);
@ -1870,8 +1866,10 @@ pref("browser.newtabpage.activity-stream.discoverystream.recs.personalized", fal
// System pref to allow Pocket sponsored content personalization to be turned on/off.
pref("browser.newtabpage.activity-stream.discoverystream.spocs.personalized", true);
// System pref to enable topic selection for pocket feed
// System pref to enable topic selection for Pocket feed
pref("browser.newtabpage.activity-stream.discoverystream.topicSelection.enabled", false);
// System pref to enable topic labels on Pocket cards
pref("browser.newtabpage.activity-stream.discoverystream.topicLabels.enabled", false);
// Flip this once the user has dismissed the Pocket onboarding experience,
pref("browser.newtabpage.activity-stream.discoverystream.onboardingExperience.dismissed", false);

View file

@ -17,8 +17,9 @@ using GlobalInitializerFn = void(__cdecl*)(void);
// Allocation of static initialization section for the freestanding library
#pragma section(".freestd$a", read)
__declspec(allocate(".freestd$a")) static const GlobalInitializerFn
FreeStdStart = reinterpret_cast<GlobalInitializerFn>(0);
__declspec(allocate(
".freestd$a")) static const GlobalInitializerFn FreeStdStart =
reinterpret_cast<GlobalInitializerFn>(0);
#pragma section(".freestd$z", read)
__declspec(allocate(".freestd$z")) static const GlobalInitializerFn FreeStdEnd =

View file

@ -854,7 +854,10 @@ var gIdentityHandler = {
if (this._isMixedActiveContentLoaded) {
this._identityBox.classList.add("mixedActiveContent");
if (UrlbarPrefs.get("trimHttps") && warnTextOnInsecure) {
if (
UrlbarPrefs.getScotchBonnetPref("trimHttps") &&
warnTextOnInsecure
) {
icon_label = gNavigatorBundle.getString("identity.notSecure.label");
this._identityBox.classList.add("notSecureText");
}

View file

@ -2634,24 +2634,6 @@ export class nsContextMenu {
this.window.openTrustedLinkIn(drmInfoURL, dest);
}
/**
* Determines if Full Page Translations is currently active on this page.
*
* @returns {boolean}
*/
#isFullPageTranslationsActive() {
try {
const { requestedTranslationPair } =
lazy.TranslationsParent.getTranslationsActor(
this.browser
).languageState;
return requestedTranslationPair !== null;
} catch {
// Failed to retrieve the Full Page Translations actor, do nothing.
}
return false;
}
/**
* Opens the SelectTranslationsPanel singleton instance.
*
@ -2697,6 +2679,7 @@ export class nsContextMenu {
}
if (displayName) {
translateSelectionItem.setAttribute("target-language", toLanguage);
this.document.l10n.setAttributes(
translateSelectionItem,
this.isTextSelected
@ -2710,6 +2693,7 @@ export class nsContextMenu {
// Either no to-language exists, or an error occurred,
// so localize the menuitem without a target language.
translateSelectionItem.removeAttribute("target-language");
this.document.l10n.setAttributes(
translateSelectionItem,
this.isTextSelected
@ -2769,11 +2753,10 @@ export class nsContextMenu {
// Only show the item if Translations is supported on this hardware.
!lazy.TranslationsParent.getIsTranslationsEngineSupported() ||
// If there is no text to translate, we have nothing to do.
textToTranslate.length === 0 ||
// We do not allow translating selections on top of Full Page Translations.
this.#isFullPageTranslationsActive();
textToTranslate.length === 0;
if (translateSelectionItem.hidden) {
translateSelectionItem.removeAttribute("target-language");
return;
}

View file

@ -258,14 +258,7 @@ div#feature-callout.hidden {
#feature-callout .onboardingContainer .outer-wrapper {
--transition: none;
height: auto;
}
#feature-callout:dir(rtl) {
transform: none;
direction: ltr;
}
#feature-callout .outer-wrapper:dir(rtl) {
transform: none;
direction: rtl;
}
#feature-callout .screen:dir(rtl) {
transform: none;
@ -309,7 +302,7 @@ div#feature-callout.hidden {
transform: rotateY(180deg);
}
#feature-callout .screen[pos=callout] .welcome-text {
align-items: baseline;
align-items: start;
text-align: start;
margin: 0;
padding: 0;

View file

@ -127,22 +127,14 @@
// auto height to allow for arrow positioning based on height
height: auto;
}
// use a different approach to flipping to avoid the fuzzy aliasing that
// transform causes.
&:dir(rtl) {
// use a different approach to flipping to avoid the fuzzy aliasing that
// transform causes.
transform: none;
direction: ltr;
}
& .outer-wrapper:dir(rtl) {
transform: none;
direction: rtl;
}
.screen {
// override transform in about:welcome
// override the RTL transform in about:welcome
&:dir(rtl) {
transform: none;
}
@ -198,7 +190,7 @@
}
.welcome-text {
align-items: baseline;
align-items: start;
text-align: start;
margin: 0;
padding: 0;

View file

@ -14,10 +14,10 @@ class nsKeychainMigrationUtils : public nsIKeychainMigrationUtils {
NS_DECL_ISUPPORTS
NS_DECL_NSIKEYCHAINMIGRATIONUTILS
nsKeychainMigrationUtils(){};
nsKeychainMigrationUtils() {};
protected:
virtual ~nsKeychainMigrationUtils(){};
virtual ~nsKeychainMigrationUtils() {};
};
#endif

View file

@ -14,6 +14,7 @@ import { connect, useSelector } from "react-redux";
const PREF_ONBOARDING_EXPERIENCE_DISMISSED =
"discoverystream.onboardingExperience.dismissed";
const PREF_THUMBS_UP_DOWN_ENABLED = "discoverystream.thumbsUpDown.enabled";
const PREF_TOPICS_ENABLED = "discoverystream.topicLabels.enabled";
const INTERSECTION_RATIO = 0.5;
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
@ -335,6 +336,7 @@ export class _CardGrid extends React.PureComponent {
const isOnboardingExperienceDismissed =
prefs[PREF_ONBOARDING_EXPERIENCE_DISMISSED];
const mayHaveThumbsUpDown = prefs[PREF_THUMBS_UP_DOWN_ENABLED];
const showTopics = prefs[PREF_TOPICS_ENABLED];
const recs = this.props.data.recommendations.slice(0, items);
const cards = [];
@ -356,6 +358,8 @@ export class _CardGrid extends React.PureComponent {
word_count={rec.word_count}
time_to_read={rec.time_to_read}
title={rec.title}
topic={rec.topic}
showTopics={showTopics}
excerpt={rec.excerpt}
url={rec.url}
id={rec.id}

View file

@ -559,6 +559,9 @@ export class _DSCard extends React.PureComponent {
className={`ds-card ${compactImagesClassName} ${imageGradientClassName} ${titleLinesName} ${descLinesClassName} ${ctaButtonClassName} ${ctaButtonVariantClassName}`}
ref={this.setContextMenuButtonHostRef}
>
{this.props.showTopics && this.props.topic && (
<span className="ds-card-topic">{this.props.topic}</span>
)}
<div className="img-wrapper">
<DSImage
extraClassNames="img"

View file

@ -174,6 +174,16 @@ $ds-card-image-gradient-solid: rgba(0, 0, 0, 100%);
}
}
.ds-card-topic {
position: absolute;
z-index: 1;
background: light-dark(#F0F0F4, var(--newtab-background-color-secondary));
border-radius: var(--border-radius-small);
padding: var(--space-small);
margin: var(--space-small);
font-size: 14px;
}
.meta {
display: flex;
flex-direction: column;

View file

@ -4501,6 +4501,15 @@ main section {
box-shadow: 0 0 0 3px var(--newtab-primary-action-background-dimmed), 0 0 0 1px var(--newtab-primary-action-background);
transition: none;
}
.ds-card .ds-card-topic {
position: absolute;
z-index: 1;
background: light-dark(#F0F0F4, var(--newtab-background-color-secondary));
border-radius: var(--border-radius-small);
padding: var(--space-small);
margin: var(--space-small);
font-size: 14px;
}
.ds-card .meta {
display: flex;
flex-direction: column;

View file

@ -4505,6 +4505,15 @@ main section {
box-shadow: 0 0 0 3px var(--newtab-primary-action-background-dimmed), 0 0 0 1px var(--newtab-primary-action-background);
transition: none;
}
.ds-card .ds-card-topic {
position: absolute;
z-index: 1;
background: light-dark(#F0F0F4, var(--newtab-background-color-secondary));
border-radius: var(--border-radius-small);
padding: var(--space-small);
margin: var(--space-small);
font-size: 14px;
}
.ds-card .meta {
display: flex;
flex-direction: column;

View file

@ -4501,6 +4501,15 @@ main section {
box-shadow: 0 0 0 3px var(--newtab-primary-action-background-dimmed), 0 0 0 1px var(--newtab-primary-action-background);
transition: none;
}
.ds-card .ds-card-topic {
position: absolute;
z-index: 1;
background: light-dark(#F0F0F4, var(--newtab-background-color-secondary));
border-radius: var(--border-radius-small);
padding: var(--space-small);
margin: var(--space-small);
font-size: 14px;
}
.ds-card .meta {
display: flex;
flex-direction: column;

View file

@ -3213,7 +3213,9 @@ class _DSCard extends (external_React_default()).PureComponent {
return /*#__PURE__*/external_React_default().createElement("article", {
className: `ds-card ${compactImagesClassName} ${imageGradientClassName} ${titleLinesName} ${descLinesClassName} ${ctaButtonClassName} ${ctaButtonVariantClassName}`,
ref: this.setContextMenuButtonHostRef
}, /*#__PURE__*/external_React_default().createElement("div", {
}, this.props.showTopics && this.props.topic && /*#__PURE__*/external_React_default().createElement("span", {
className: "ds-card-topic"
}, this.props.topic), /*#__PURE__*/external_React_default().createElement("div", {
className: "img-wrapper"
}, /*#__PURE__*/external_React_default().createElement(DSImage, {
extraClassNames: "img",
@ -3578,6 +3580,7 @@ const TopicsWidget = (0,external_ReactRedux_namespaceObject.connect)(state => ({
const PREF_ONBOARDING_EXPERIENCE_DISMISSED = "discoverystream.onboardingExperience.dismissed";
const PREF_THUMBS_UP_DOWN_ENABLED = "discoverystream.thumbsUpDown.enabled";
const PREF_TOPICS_ENABLED = "discoverystream.topicLabels.enabled";
const CardGrid_INTERSECTION_RATIO = 0.5;
const CardGrid_VISIBLE = "visible";
const CardGrid_VISIBILITY_CHANGE_EVENT = "visibilitychange";
@ -3851,6 +3854,7 @@ class _CardGrid extends (external_React_default()).PureComponent {
const showRecentSaves = prefs.showRecentSaves && recentSavesEnabled;
const isOnboardingExperienceDismissed = prefs[PREF_ONBOARDING_EXPERIENCE_DISMISSED];
const mayHaveThumbsUpDown = prefs[PREF_THUMBS_UP_DOWN_ENABLED];
const showTopics = prefs[PREF_TOPICS_ENABLED];
const recs = this.props.data.recommendations.slice(0, items);
const cards = [];
let essentialReadsCards = [];
@ -3868,6 +3872,8 @@ class _CardGrid extends (external_React_default()).PureComponent {
word_count: rec.word_count,
time_to_read: rec.time_to_read,
title: rec.title,
topic: rec.topic,
showTopics: showTopics,
excerpt: rec.excerpt,
url: rec.url,
id: rec.id,

View file

@ -195,7 +195,7 @@ module.exports = function (config) {
statements: 98.25,
lines: 98.2,
functions: 100,
branches: 74.81,
branches: 74.63,
},
"content-src/components/DiscoveryStreamComponents/**/*.jsx": {
statements: 90.48,

View file

@ -555,6 +555,13 @@ export const PREFS_CONFIG = new Map([
value: "business, arts, government",
},
],
[
"discoverystream.topicLabels.enabled",
{
title: "Enables topic labels for discovery stream",
value: false,
},
],
[
"showRecentSaves",
{

View file

@ -1411,6 +1411,7 @@ export class DiscoveryStreamFeed {
scheduled_corpus_item_id: item.scheduledCorpusItemId,
url: item.url,
title: item.title,
topic: item.topic,
excerpt: item.excerpt,
publisher: item.publisher,
raw_image_src: item.imageUrl,

View file

@ -26,13 +26,6 @@ const { DiscoveryStreamFeed } = ChromeUtils.importESModule(
);
SearchTestUtils.init(this);
AddonTestUtils.init(this);
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"42",
"42"
);
const { AboutNewTab } = ChromeUtils.importESModule(
"resource:///modules/AboutNewTab.sys.mjs"
@ -61,7 +54,7 @@ add_setup(async function () {
do_get_profile();
// The SearchService is also needed in order to construct the initial state,
// which means that the AddonManager needs to be available.
await AddonTestUtils.promiseStartupManager();
await SearchTestUtils.initXPCShellAddonManager();
// The example.com domain will be used to host the dynamic layout JSON and
// the top stories JSON.

View file

@ -10,20 +10,20 @@
class="subcategory"
hidden="true"
data-category="paneExperimental">
<html:h1 data-l10n-id="settings-pane-labs-title"/>
<html:h1 data-l10n-id="settings-pane-labs-title" class="section-heading"/>
</vbox>
<html:div data-category="paneExperimental"
id="pane-experimental-featureGates"
hidden="true">
<label class="search-header" hidden="true">
<html:h2 data-l10n-id="settings-pane-labs-title"/>
<html:h2 data-l10n-id="settings-pane-labs-title" class="section-heading"/>
</label>
<html:p data-l10n-id="pane-experimental-description3" class="description-deemphasized"/>
<hbox pack="end">
<button id="experimentalCategory-reset"
class="accessory-button"
data-l10n-id="pane-experimental-reset"/>
<html:p data-l10n-id="pane-experimental-description3"
class="description-deemphasized section-description"/>
<hbox pack="start" class="section-header-last">
<html:moz-button id="experimentalCategory-reset"
data-l10n-id="pane-experimental-reset"/>
</hbox>
</html:div>
</html:template>

View file

@ -91,7 +91,7 @@ var gExperimentalPane = {
setEventListener(
"experimentalCategory-reset",
"command",
"click",
gExperimentalPane.resetAllFeatures
);

View file

@ -459,6 +459,26 @@ serp:
send_in_pings:
- serp-categorization
ads_blocked_count:
type: labeled_counter
description: >
Counts the specific type of block.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1907097
data_reviews:
- https://phabricator.services.mozilla.com/D216208
notification_emails:
- fx-search-telemetry@mozilla.com
- rev-data@mozilla.com
expires: never
data_sensitivity:
- technical
labels:
- beyond_viewport
- hidden_parent
- hidden_child
search_with:
reporting_url:
type: url

View file

@ -35,6 +35,8 @@ support-files = ["searchTelemetryAd_searchbox_with_content.html", "serp.css"]
["browser_search_telemetry_adImpression_component_skipCount_parent.js"]
support-files = ["searchTelemetryAd_searchbox_with_content.html", "serp.css"]
["browser_search_telemetry_adblock_count.js"]
["browser_search_telemetry_categorization_timing.js"]
["browser_search_telemetry_content.js"]

View file

@ -0,0 +1,170 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const BUILDER_URL = "https://example.com/document-builder.sjs?html=";
/**
* This HTML file contains three ads:
* - An ad that is well above the possible viewport.
* - The numbers of ads are unique to help ensure counts are correct.
*/
const TEST_URI = `
<!DOCTYPE html>
<main>
<style>
.ad_parent {
display: none;
}
/*
This is if the ad blocker doesn't block the parent component but
instead blocks the child.
*/
.ad_with_children div {
display: none;
}
.ad_far_above {
position: absolute;
top: -9999px;
}
</style>
<div class="ad_far_above">
<a href="https://example.com/ad">Ad link</a>
</div>
<div class="ad_parent">
<a href="https://example.com/ad">Ad link</a>
</div>
<div class="ad_parent">
<a href="https://example.com/ad">Ad link</a>
</div>
<div class="ad_with_children">
<span>Element</span>
<div class="child">
<a href="https://example.com/ad">Ad link</a>
</div>
<div class="child">
<a href="https://example.com/ad">Ad link</a>
</div>
<div class="child">
<a href="https://example.com/ad">Ad link</a>
</div>
</div>
</main>
`;
const URL =
"https://example.org/document-builder.sjs?html=" +
encodeURIComponent(TEST_URI) +
"&s=foobar&abc=ff";
const TEST_PROVIDER_INFO = [
{
telemetryId: "example",
searchPageRegexp: /^https:\/\/example\.org\/document-builder\.sjs/,
queryParamNames: ["s"],
codeParamName: "abc",
taggedCodes: ["ff"],
extraAdServersRegexps: [/^https:\/\/example\.com\/ad/],
components: [
{
type: SearchSERPTelemetryUtils.COMPONENTS.AD_CAROUSEL,
included: {
parent: {
selector: ".ad_with_children",
},
children: [
{
selector: ".child",
countChildren: true,
},
],
},
},
{
type: SearchSERPTelemetryUtils.COMPONENTS.AD_SITELINK,
included: {
parent: {
selector: ".ad_parent",
},
},
},
{
type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
default: true,
},
],
},
];
add_setup(async function () {
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
await waitForIdle();
// Enable local telemetry recording for the duration of the tests.
let oldCanRecord = Services.telemetry.canRecordExtended;
Services.telemetry.canRecordExtended = true;
registerCleanupFunction(async () => {
SearchSERPTelemetry.overrideSearchTelemetryForTests();
Services.telemetry.canRecordExtended = oldCanRecord;
resetTelemetry();
});
});
add_task(async function test_adblock_count() {
let { cleanup } = await openSerpInNewTab(URL);
assertSERPTelemetry([
{
impression: {
is_signed_in: "false",
is_private: "false",
source: "unknown",
is_shopping_page: "false",
partner_code: "ff",
provider: "example",
shopping_tab_displayed: "false",
tagged: "true",
},
adImpressions: [
{
component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
ads_loaded: "1",
ads_visible: "0",
ads_hidden: "1",
},
{
component: SearchSERPTelemetryUtils.COMPONENTS.AD_SITELINK,
ads_loaded: "2",
ads_visible: "0",
ads_hidden: "2",
},
{
component: SearchSERPTelemetryUtils.COMPONENTS.AD_CAROUSEL,
ads_loaded: "3",
ads_visible: "0",
ads_hidden: "3",
},
],
},
]);
await Services.fog.testFlushAllChildren();
Assert.equal(
1,
Glean.serp.adsBlockedCount.beyond_viewport.testGetValue(),
"Number of ads blocked due to being beyond the viewport."
);
Assert.equal(
2,
Glean.serp.adsBlockedCount.hidden_parent.testGetValue(),
"Number of parent elements blocked."
);
Assert.equal(
3,
Glean.serp.adsBlockedCount.hidden_child.testGetValue(),
"Number of child elements blocked."
);
await cleanup();
});

View file

@ -70,7 +70,7 @@ class AsyncFaviconDataReady final : public nsIFaviconDataCallback {
int aIconIndex, int aTimeStamp)
: mSearchResult(std::move(aSearchResult)),
mIconIndex(aIconIndex),
mTimeStamp(aTimeStamp){};
mTimeStamp(aTimeStamp) {};
private:
~AsyncFaviconDataReady() {}

View file

@ -23,7 +23,7 @@ class nsGNOMEShellSearchProvider;
class GnomeHistoryIcon {
public:
GnomeHistoryIcon() : mTimeStamp(-1), mWidth(0), mHeight(0){};
GnomeHistoryIcon() : mTimeStamp(-1), mWidth(0), mHeight(0) {};
// From which search is this icon
void Set(int aTimeStamp, mozilla::UniquePtr<uint8_t[]> aData, int aWidth,
@ -58,7 +58,7 @@ class nsGNOMEShellHistorySearchResult : public nsUnixRemoteServer {
GDBusConnection* aConnection, int aTimeStamp)
: mSearchProvider(aSearchProvider),
mConnection(aConnection),
mTimeStamp(aTimeStamp){};
mTimeStamp(aTimeStamp) {};
void SetReply(RefPtr<GDBusMethodInvocation> aReply) {
mReply = std::move(aReply);

View file

@ -16,7 +16,7 @@ class nsMacShellService : public nsIMacShellService,
public nsToolkitShellService,
public nsIWebProgressListener {
public:
nsMacShellService(){};
nsMacShellService() {};
NS_DECL_ISUPPORTS
NS_DECL_NSISHELLSERVICE
@ -24,7 +24,7 @@ class nsMacShellService : public nsIMacShellService,
NS_DECL_NSIWEBPROGRESSLISTENER
protected:
virtual ~nsMacShellService(){};
virtual ~nsMacShellService() {};
private:
nsCOMPtr<nsIFile> mBackgroundFile;

View file

@ -201,9 +201,21 @@ var SelectTranslationsPanel = new (class {
* Many of these are cases where the SelectTranslationsPanel is available
* even though the FullPageTranslationsPanel is not, so this helps inform
* whether the translate-full-page button should be allowed in this context.
*
* @type {boolean}
*/
#isFullPageTranslationsRestrictedForPage = true;
/**
* The BCP-47 language tag of the active target language for Full-Page Translations,
* if available. This may not be available if Full-Page Translations is not currently
* active in the current tab of the current window, or if Full-Page Translations is
* restricted on the current page.
*
* @type { string | undefined }
*/
#activeFullPageTranslationsTargetLanguage = undefined;
/**
* The internal state of the SelectTranslationsPanel.
*
@ -334,6 +346,13 @@ var SelectTranslationsPanel = new (class {
return this.#languageInfo;
}
this.#isFullPageTranslationsRestrictedForPage =
TranslationsParent.isFullPageTranslationsRestrictedForPage(gBrowser);
this.#activeFullPageTranslationsTargetLanguage = this
.#isFullPageTranslationsRestrictedForPage
? undefined
: this.#maybeGetActiveFullPageTranslationsTargetLanguage();
this.#languageInfo = {
docLangTag: undefined,
isDocLangTagSupported: undefined,
@ -350,7 +369,11 @@ var SelectTranslationsPanel = new (class {
const preferredLanguages = TranslationsParent.getPreferredLanguages();
const topPreferredLanguage = preferredLanguages?.[0];
this.#languageInfo = {
docLangTag,
docLangTag:
// If Full-Page Translations (FPT) is active, we need to assume that the effective
// document language tag matches the language of the FPT target language, otherwise,
// if FPT is not active, we can take the real docLangTag value.
this.#activeFullPageTranslationsTargetLanguage ?? docLangTag,
isDocLangTagSupported,
topPreferredLanguage,
};
@ -561,8 +584,6 @@ var SelectTranslationsPanel = new (class {
try {
this.#sourceTextWordCount = undefined;
this.#isFullPageTranslationsRestrictedForPage =
TranslationsParent.isFullPageTranslationsRestrictedForPage(gBrowser);
this.#initializeEventListeners();
await this.#ensureLangListsBuilt();
await Promise.all([
@ -586,6 +607,26 @@ var SelectTranslationsPanel = new (class {
this.#openPopup(event, screenX, screenY);
}
/**
* Attempts to retrieve the language tag of the requested target language
* for Full Page Translations, if Full Page Translations is active on the page
* within the active tab of the active window.
*
* @returns {string | undefined} - The BCP-47 language tag.
*/
#maybeGetActiveFullPageTranslationsTargetLanguage() {
try {
const { requestedTranslationPair } =
TranslationsParent.getTranslationsActor(
gBrowser.selectedBrowser
).languageState;
return requestedTranslationPair?.toLanguage;
} catch {
this.console.warn("Failed to retrieve the TranslationsParent actor.");
}
return undefined;
}
/**
* Forces the panel to close and reopen at the same location.
*
@ -1768,6 +1809,20 @@ var SelectTranslationsPanel = new (class {
}
}
/**
* Returns true if the translate-full-page button should be hidden in the current panel view.
*
* @returns {boolean}
*/
#shouldHideTranslateFullPageButton() {
return (
// Do not offer to translate the full page if it is restricted on this page.
this.#isFullPageTranslationsRestrictedForPage ||
// Do not offer to translate the full page if Full-Page Translations is already active.
this.#activeFullPageTranslationsTargetLanguage
);
}
/**
* Determines whether translation should continue based on panel state and language pair.
*
@ -1874,7 +1929,7 @@ var SelectTranslationsPanel = new (class {
translateFullPageButton.disabled =
invalidLangPairSelected ||
fromLanguage === toLanguage ||
this.#isFullPageTranslationsRestrictedForPage;
this.#shouldHideTranslateFullPageButton();
}
/**
@ -1917,7 +1972,7 @@ var SelectTranslationsPanel = new (class {
translationFailureMessageBar,
tryAgainButton,
unsupportedLanguageContent,
...(this.#isFullPageTranslationsRestrictedForPage
...(this.#shouldHideTranslateFullPageButton()
? [translateFullPageButton]
: []),
],
@ -1926,7 +1981,7 @@ var SelectTranslationsPanel = new (class {
copyButton,
doneButtonPrimary,
textArea,
...(this.#isFullPageTranslationsRestrictedForPage
...(this.#shouldHideTranslateFullPageButton()
? []
: [translateFullPageButton]),
],
@ -2127,16 +2182,10 @@ var SelectTranslationsPanel = new (class {
* @returns {Promise<MessagePort | undefined>} The message port promise.
*/
async #requestTranslationsPort(fromLanguage, toLanguage) {
const innerWindowId =
gBrowser.selectedBrowser.browsingContext.top.embedderElement
.innerWindowID;
if (!innerWindowId) {
return undefined;
}
const port = await TranslationsParent.requestTranslationsPort(
innerWindowId,
fromLanguage,
toLanguage
toLanguage,
/* innerWindowId */ null
);
return port;
}

View file

@ -107,6 +107,12 @@ skip-if = ["os == 'linux' && !debug"] # Bug 1863227
["browser_translations_select_context_menu_feature_disabled.js"]
["browser_translations_select_context_menu_preferred_app_locales.js"]
["browser_translations_select_context_menu_preferred_language_edge_cases.js"]
["browser_translations_select_context_menu_preferred_web_languages.js"]
["browser_translations_select_context_menu_with_full_page_translations_active.js"]
["browser_translations_select_context_menu_with_hyperlink.js"]
@ -199,4 +205,8 @@ skip-if = ["os == 'linux' && !debug"] # Bug 1863227
["browser_translations_select_telemetry_translation_failure_ui_then_succeed.js"]
["browser_translations_select_telemetry_translation_failure_with_full_page_translations_active.js"]
["browser_translations_select_telemetry_translation_success_with_full_page_translations_active.js"]
["browser_translations_select_telemetry_unsupported_language_ui.js"]

View file

@ -0,0 +1,64 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* This test case tests various fallback edge cases regarding which language to
* offer for translations based on the user's application locale settings.
*/
add_task(
async function test_translate_selection_menuitem_preferred_app_locales() {
const { cleanup, runInPage } = await loadTestPage({
page: SELECT_TEST_PAGE_URL,
languagePairs: [
{ fromLang: "en", toLang: "es" },
{ fromLang: "es", toLang: "en" },
{ fromLang: "en", toLang: "fr" },
{ fromLang: "fr", toLang: "en" },
{ fromLang: "en", toLang: "pl" },
{ fromLang: "pl", toLang: "en" },
// Only supported as a source language
{ fromLang: "fi", toLang: "en" },
// Only supported as a target language
{ fromLang: "en", toLang: "sl" },
],
prefs: [["browser.translations.select.enable", true]],
});
await FullPageTranslationsTestUtils.assertPageIsUntranslated(runInPage);
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["es", "fr", "fi", "zh", "sl"],
expectedTargetLanguage: "es",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["fr", "fi", "zh", "sl", "es"],
expectedTargetLanguage: "fr",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["fi", "zh", "sl", "es", "fr"],
// "fi" is not supported as a target language, so fall back
expectedTargetLanguage: "sl",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["zh", "sl", "es", "fr", "fi"],
expectedTargetLanguage: "sl",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["sl", "es", "fr", "fi", "zh"],
expectedTargetLanguage: "sl",
});
await cleanup();
}
);

View file

@ -0,0 +1,74 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* This test case tests various fallback edge cases regarding which language to
* offer for translations based on user settings and supported translations languages.
*/
add_task(
async function test_translate_selection_menuitem_preferred_language_edge_cases() {
const { cleanup, runInPage } = await loadTestPage({
page: SELECT_TEST_PAGE_URL,
languagePairs: [
{ fromLang: "en", toLang: "es" },
{ fromLang: "es", toLang: "en" },
{ fromLang: "en", toLang: "fr" },
{ fromLang: "fr", toLang: "en" },
{ fromLang: "en", toLang: "pl" },
{ fromLang: "pl", toLang: "en" },
// Only supported as a source language
{ fromLang: "fi", toLang: "en" },
// Only supported as a target language
{ fromLang: "en", toLang: "sl" },
],
prefs: [["browser.translations.select.enable", true]],
});
await FullPageTranslationsTestUtils.assertPageIsUntranslated(runInPage);
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
systemLocales: [],
appLocales: [],
webLanguages: [],
// No locales are specified, so fall back to "en".
expectedTargetLanguage: "en",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["fi", "fr", "en-US"],
webLanguages: ["zh"],
expectedTargetLanguage: "fr",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["zh", "uk", "fr", "en-US"],
webLanguages: ["fi"],
// Fall back to the first to-language compatible tag.
expectedTargetLanguage: "fr",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
appLocales: ["zh", "fi", "sl", "fr", "en-US"],
webLanguages: ["fi", "zh"],
// Fall back to the first to-language compatible tag.
expectedTargetLanguage: "sl",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
systemLocales: ["zh-TW", "zh-CN", "de"],
appLocales: ["pt-BR", "ja"],
webLanguages: ["cs", "hu"],
// None of these locales are supported, so default to "en".
expectedTargetLanguage: "en",
});
await cleanup();
}
);

View file

@ -0,0 +1,64 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* This test case tests various fallback edge cases regarding which language to
* offer for translations based on the user's web content language settings.
*/
add_task(
async function test_translate_selection_menuitem_preferred_web_languages() {
const { cleanup, runInPage } = await loadTestPage({
page: SELECT_TEST_PAGE_URL,
languagePairs: [
{ fromLang: "en", toLang: "es" },
{ fromLang: "es", toLang: "en" },
{ fromLang: "en", toLang: "fr" },
{ fromLang: "fr", toLang: "en" },
{ fromLang: "en", toLang: "pl" },
{ fromLang: "pl", toLang: "en" },
// Only supported as a source language
{ fromLang: "fi", toLang: "en" },
// Only supported as a target language
{ fromLang: "en", toLang: "sl" },
],
prefs: [["browser.translations.select.enable", true]],
});
await FullPageTranslationsTestUtils.assertPageIsUntranslated(runInPage);
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
webLanguages: ["es", "fr", "fi", "zh", "sl"],
expectedTargetLanguage: "es",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
webLanguages: ["fr", "fi", "zh", "sl", "es"],
expectedTargetLanguage: "fr",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
webLanguages: ["fi", "zh", "sl", "es", "fr"],
// "fi" is not supported as a target language, so fall back
expectedTargetLanguage: "sl",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
webLanguages: ["zh", "sl", "es", "fr", "fi"],
expectedTargetLanguage: "sl",
});
await SelectTranslationsTestUtils.testContextMenuItemWithLocales({
runInPage,
webLanguages: ["sl", "es", "fr", "fi", "zh"],
expectedTargetLanguage: "sl",
});
await cleanup();
}
);

View file

@ -54,9 +54,10 @@ add_task(
{
selectSpanishSentence: true,
openAtSpanishSentence: true,
expectMenuItemVisible: false,
expectMenuItemVisible: true,
expectedTargetLanguage: "en",
},
"The translate-selection context menu item should be unavailable while full-page translations is active."
"The translate-selection context menu item should be available even while full-page translations is active."
);
await FullPageTranslationsTestUtils.openPanel({
@ -133,9 +134,10 @@ add_task(
{
selectSpanishSentence: false,
openAtSpanishHyperlink: true,
expectMenuItemVisible: false,
expectMenuItemVisible: true,
expectedTargetLanguage: "en",
},
"The translate-selection context menu item should be unavailable while full-page translations is active."
"The translate-selection context menu item should be available even while full-page translations is active."
);
await FullPageTranslationsTestUtils.openPanel({

View file

@ -0,0 +1,205 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(
async function test_select_translations_panel_translation_failure_with_full_page_translations_active() {
const { cleanup, runInPage, resolveDownloads, rejectDownloads } =
await loadTestPage({
page: SELECT_TEST_PAGE_URL,
languagePairs: LANGUAGE_PAIRS,
prefs: [["browser.translations.select.enable", true]],
});
await SelectTranslationsTestUtils.openPanel(runInPage, {
openAtSpanishHyperlink: true,
expectedFromLanguage: "es",
expectedToLanguage: "en",
downloadHandler: resolveDownloads,
onOpenPanel: SelectTranslationsTestUtils.assertPanelViewTranslated,
});
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.open,
{
expectedEventCount: 1,
expectNewFlowId: true,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "en",
top_preferred_language: "en",
text_source: "hyperlink",
},
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 0],
["select", 1],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 1,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "en",
top_preferred_language: "en",
request_target: "select",
auto_translate: false,
source_text_code_units: 23,
source_text_word_count: 4,
},
}
);
await SelectTranslationsTestUtils.changeSelectedToLanguage(["fr"], {
openDropdownMenu: true,
pivotTranslation: true,
downloadHandler: resolveDownloads,
onChangeLanguage: SelectTranslationsTestUtils.assertPanelViewTranslated,
});
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.changeToLanguage,
{
expectedEventCount: 1,
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 0],
["select", 2],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 2,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "fr",
top_preferred_language: "en",
request_target: "select",
auto_translate: false,
source_text_code_units: 23,
source_text_word_count: 4,
},
}
);
await SelectTranslationsTestUtils.clickTranslateFullPageButton();
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.translateFullPageButton,
{
expectedEventCount: 1,
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 1],
["select", 2],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 3,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "fr",
top_preferred_language: "en",
request_target: "full_page",
auto_translate: false,
},
}
);
await FullPageTranslationsTestUtils.assertPageIsTranslated(
"es",
"fr",
runInPage
);
await SelectTranslationsTestUtils.openPanel(runInPage, {
selectFrenchSection: true,
openAtFrenchSection: true,
expectedFromLanguage: "fr",
expectedToLanguage: "en",
downloadHandler: rejectDownloads,
onOpenPanel:
SelectTranslationsTestUtils.assertPanelViewTranslationFailure,
});
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.open,
{
expectedEventCount: 2,
expectNewFlowId: true,
assertForMostRecentEvent: {
document_language: "fr",
from_language: "fr",
to_language: "en",
top_preferred_language: "en",
text_source: "selection",
},
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 1],
["select", 3],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 4,
assertForMostRecentEvent: {
document_language: "fr",
from_language: "fr",
to_language: "en",
top_preferred_language: "en",
request_target: "select",
auto_translate: false,
source_text_code_units:
AppConstants.platform === "win"
? 1718 // With carriage returns
: 1709, // No carriage returns
source_text_word_count: 281,
},
}
);
await SelectTranslationsTestUtils.clickCancelButton();
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.cancelButton,
{
expectedEventCount: 1,
}
);
await FullPageTranslationsTestUtils.openPanel({
onOpenPanel: FullPageTranslationsTestUtils.assertPanelViewRevisit,
});
await TestTranslationsTelemetry.assertEvent(Glean.translationsPanel.open, {
expectedEventCount: 1,
expectNewFlowId: true,
assertForMostRecentEvent: {
auto_show: false,
view_name: "revisitView",
opened_from: "translationsButton",
document_language: "es",
},
});
await cleanup();
}
);

View file

@ -0,0 +1,203 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(
async function test_select_translations_panel_translation_success_with_full_page_translations_active() {
const { cleanup, runInPage, resolveDownloads } = await loadTestPage({
page: SELECT_TEST_PAGE_URL,
languagePairs: LANGUAGE_PAIRS,
prefs: [["browser.translations.select.enable", true]],
});
await SelectTranslationsTestUtils.openPanel(runInPage, {
openAtSpanishHyperlink: true,
expectedFromLanguage: "es",
expectedToLanguage: "en",
downloadHandler: resolveDownloads,
onOpenPanel: SelectTranslationsTestUtils.assertPanelViewTranslated,
});
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.open,
{
expectedEventCount: 1,
expectNewFlowId: true,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "en",
top_preferred_language: "en",
text_source: "hyperlink",
},
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 0],
["select", 1],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 1,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "en",
top_preferred_language: "en",
request_target: "select",
auto_translate: false,
source_text_code_units: 23,
source_text_word_count: 4,
},
}
);
await SelectTranslationsTestUtils.changeSelectedToLanguage(["fr"], {
openDropdownMenu: true,
pivotTranslation: true,
downloadHandler: resolveDownloads,
onChangeLanguage: SelectTranslationsTestUtils.assertPanelViewTranslated,
});
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.changeToLanguage,
{
expectedEventCount: 1,
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 0],
["select", 2],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 2,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "fr",
top_preferred_language: "en",
request_target: "select",
auto_translate: false,
source_text_code_units: 23,
source_text_word_count: 4,
},
}
);
await SelectTranslationsTestUtils.clickTranslateFullPageButton();
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.translateFullPageButton,
{
expectedEventCount: 1,
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 1],
["select", 2],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 3,
assertForMostRecentEvent: {
document_language: "es",
from_language: "es",
to_language: "fr",
top_preferred_language: "en",
request_target: "full_page",
auto_translate: false,
},
}
);
await FullPageTranslationsTestUtils.assertPageIsTranslated(
"es",
"fr",
runInPage
);
await SelectTranslationsTestUtils.openPanel(runInPage, {
selectFrenchSection: true,
openAtFrenchSection: true,
expectedFromLanguage: "fr",
expectedToLanguage: "en",
downloadHandler: resolveDownloads,
onOpenPanel: SelectTranslationsTestUtils.assertPanelViewTranslated,
});
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.open,
{
expectedEventCount: 2,
expectNewFlowId: true,
assertForMostRecentEvent: {
document_language: "fr",
from_language: "fr",
to_language: "en",
top_preferred_language: "en",
text_source: "selection",
},
}
);
await TestTranslationsTelemetry.assertLabeledCounter(
Glean.translations.requestCount,
[
["full_page", 1],
["select", 3],
]
);
await TestTranslationsTelemetry.assertEvent(
Glean.translations.translationRequest,
{
expectedEventCount: 4,
assertForMostRecentEvent: {
document_language: "fr",
from_language: "fr",
to_language: "en",
top_preferred_language: "en",
request_target: "select",
auto_translate: false,
source_text_code_units:
AppConstants.platform === "win"
? 1718 // With carriage returns
: 1709, // No carriage returns
source_text_word_count: 281,
},
}
);
await SelectTranslationsTestUtils.clickDoneButton();
await TestTranslationsTelemetry.assertEvent(
Glean.translationsSelectTranslationsPanel.doneButton,
{
expectedEventCount: 1,
}
);
await FullPageTranslationsTestUtils.openPanel({
onOpenPanel: FullPageTranslationsTestUtils.assertPanelViewRevisit,
});
await TestTranslationsTelemetry.assertEvent(Glean.translationsPanel.open, {
expectedEventCount: 1,
expectNewFlowId: true,
assertForMostRecentEvent: {
auto_show: false,
view_name: "revisitView",
opened_from: "translationsButton",
document_language: "es",
},
});
await cleanup();
}
);

View file

@ -199,6 +199,24 @@ function logAction(...params) {
);
}
/**
* Returns true if Full-Page Translations is currently active, otherwise false.
*
* @returns {boolean}
*/
function isFullPageTranslationsActive() {
try {
const { requestedTranslationPair } =
TranslationsParent.getTranslationsActor(
gBrowser.selectedBrowser
).languageState;
return !!requestedTranslationPair;
} catch {
// Translations actor unavailable, continue on.
}
return false;
}
/**
* Navigate to a URL and indicate a message as to why.
*/
@ -1513,6 +1531,8 @@ class SelectTranslationsTestUtils {
if (expectedTargetLanguage) {
// Target language expected, check for the data-l10n-id with a `{$language}` argument.
const expectedL10nId =
selectH1 ||
selectPdfSpan ||
selectFrenchSection ||
selectEnglishSection ||
selectSpanishSection ||
@ -1521,26 +1541,34 @@ class SelectTranslationsTestUtils {
selectSpanishSentence
? "main-context-menu-translate-selection-to-language"
: "main-context-menu-translate-link-text-to-language";
await waitForCondition(
() =>
menuItem.getAttribute("target-language") === expectedTargetLanguage,
`Waiting for translate-selection context menu item to match the expected target language ${expectedTargetLanguage}`
);
await waitForCondition(
() => menuItem.getAttribute("data-l10n-id") === expectedL10nId,
`Waiting for translate-selection context menu item to localize with target language ${expectedTargetLanguage}`
`Waiting for translate-selection context menu item to have the correct data-l10n-id '${expectedL10nId}`
);
is(
menuItem.getAttribute("data-l10n-id"),
expectedL10nId,
"Expected the translate-selection context menu item to be localized with a target language."
);
const l10nArgs = JSON.parse(menuItem.getAttribute("data-l10n-args"));
is(
l10nArgs.language,
getIntlDisplayName(expectedTargetLanguage),
`Expected the translate-selection context menu item to have the target language '${expectedTargetLanguage}'.`
);
if (Services.locale.appLocaleAsBCP47 === "en-US") {
// We only want to test the localized name in CI if the current app locale is the default (en-US).
const expectedLanguageDisplayName = getIntlDisplayName(
expectedTargetLanguage
);
await waitForCondition(() => {
const l10nArgs = JSON.parse(
menuItem.getAttribute("data-l10n-args")
);
return l10nArgs.language === expectedLanguageDisplayName;
}, `Waiting for translate-selection context menu item to have the correct data-l10n-args '${expectedLanguageDisplayName}`);
}
} else {
// No target language expected, check for the data-l10n-id that has no `{$language}` argument.
const expectedL10nId =
selectH1 ||
selectPdfSpan ||
selectFrenchSection ||
selectEnglishSection ||
selectSpanishSection ||
@ -1550,19 +1578,56 @@ class SelectTranslationsTestUtils {
? "main-context-menu-translate-selection"
: "main-context-menu-translate-link-text";
await waitForCondition(
() => menuItem.getAttribute("data-l10n-id") === expectedL10nId,
"Waiting for translate-selection context menu item to localize without target language."
() => !menuItem.getAttribute("target-language"),
"Waiting for translate-selection context menu item to remove its target-language attribute."
);
is(
menuItem.getAttribute("data-l10n-id"),
expectedL10nId,
"Expected the translate-selection context menu item to be localized without a target language."
await waitForCondition(
() => menuItem.getAttribute("data-l10n-id") === expectedL10nId,
`Waiting for translate-selection context menu item to have the correct data-l10n-id '${expectedL10nId}`
);
}
}
}
/**
* Tests that the context menu displays the expected target language for translation based on
* the provided configurations.
*
* @param {object} options - Options for configuring the test environment and expected language behavior.
* @param {Array.<string>} options.runInPage - A content-exposed function to run within the context of the page.
* @param {Array.<string>} [options.systemLocales=[]] - Locales to mock as system locales.
* @param {Array.<string>} [options.appLocales=[]] - Locales to mock as application locales.
* @param {Array.<string>} [options.webLanguages=[]] - Languages to mock as web languages.
* @param {string} options.expectedTargetLanguage - The expected target language for the translate-selection item.
*/
static async testContextMenuItemWithLocales({
runInPage,
systemLocales = [],
appLocales = [],
webLanguages = [],
expectedTargetLanguage,
}) {
const cleanupLocales = await mockLocales({
systemLocales,
appLocales,
webLanguages,
});
await SelectTranslationsTestUtils.assertContextMenuTranslateSelectionItem(
runInPage,
{
selectSpanishSentence: true,
openAtSpanishSentence: true,
expectMenuItemVisible: true,
expectedTargetLanguage,
},
`The translate-selection context menu item should match the expected target language '${expectedTargetLanguage}'`
);
await closeAllOpenPanelsAndMenus();
await cleanupLocales();
}
/**
* Asserts that for each provided expectation, the visible state of the corresponding
* element in FullPageTranslationsPanel.elements both exists and matches the visibility expectation.
@ -1660,14 +1725,20 @@ class SelectTranslationsTestUtils {
textArea: true,
toLabel: true,
toMenuList: true,
translateFullPageButton: !isFullPageTranslationsRestrictedForPage,
translateFullPageButton: !(
isFullPageTranslationsRestrictedForPage ||
isFullPageTranslationsActive()
),
});
SelectTranslationsTestUtils.#assertConditionalUIEnabled({
copyButton: true,
doneButtonPrimary: true,
textArea: true,
translateFullPageButton:
!sameLanguageSelected && !isFullPageTranslationsRestrictedForPage,
translateFullPageButton: !(
sameLanguageSelected ||
isFullPageTranslationsRestrictedForPage ||
isFullPageTranslationsActive()
),
});
await waitForCondition(
@ -1683,7 +1754,11 @@ class SelectTranslationsTestUtils {
await SelectTranslationsTestUtils.#assertPanelTextAreaOverflow();
let footerButtons;
if (sameLanguageSelected || isFullPageTranslationsRestrictedForPage) {
if (
sameLanguageSelected ||
isFullPageTranslationsRestrictedForPage ||
isFullPageTranslationsActive()
) {
footerButtons = [copyButton, doneButtonPrimary];
} else {
footerButtons =
@ -1914,7 +1989,10 @@ class SelectTranslationsTestUtils {
textArea: true,
toLabel: true,
toMenuList: true,
translateFullPageButton: !isFullPageTranslationsRestrictedForPage,
translateFullPageButton: !(
isFullPageTranslationsRestrictedForPage ||
isFullPageTranslationsActive()
),
});
SelectTranslationsTestUtils.#assertPanelHasTranslatingPlaceholder();
}
@ -1943,7 +2021,8 @@ class SelectTranslationsTestUtils {
doneButtonPrimary: true,
translateFullPageButton:
fromMenuList.value !== toMenuList.value &&
!isFullPageTranslationsRestrictedForPage,
!isFullPageTranslationsRestrictedForPage &&
!isFullPageTranslationsActive(),
});
}
@ -1965,7 +2044,9 @@ class SelectTranslationsTestUtils {
copyButton: true,
doneButtonPrimary: true,
translateFullPageButton:
fromLanguage !== toLanguage && !isFullPageTranslationsRestrictedForPage,
fromLanguage !== toLanguage &&
!isFullPageTranslationsRestrictedForPage &&
!isFullPageTranslationsActive(),
});
if (fromLanguage === toLanguage) {

View file

@ -207,8 +207,8 @@ class _QuickSuggest {
}
}
this._updateFeatureState();
lazy.NimbusFeatures.urlbar.onUpdate(() => this._updateFeatureState());
this.#updateAll();
lazy.NimbusFeatures.urlbar.onUpdate(() => this.#updateAll());
lazy.UrlbarPrefs.addObserver(this);
}
@ -518,7 +518,7 @@ class _QuickSuggest {
/**
* Updates state based on whether quick suggest and its features are enabled.
*/
_updateFeatureState() {
#updateAll() {
// IMPORTANT: This method is a `NimbusFeatures.urlbar.onUpdate()` callback,
// which means it's called on every change to any pref that is a fallback
// for a urlbar Nimbus variable.

View file

@ -238,6 +238,7 @@ export class UrlbarInput {
this.window.addEventListener("draggableregionleftmousedown", this);
}
this.textbox.addEventListener("mousedown", this);
this.textbox.addEventListener("mouseup", this);
// This listener handles clicks from our children too, included the search mode
// indicator close button.
@ -3196,7 +3197,9 @@ export class UrlbarInput {
#maybeUntrimUrl({ moveCursorToStart = false } = {}) {
// Check if we can untrim the current value.
if (
!lazy.UrlbarPrefs.get("untrimOnUserInteraction.featureGate") ||
!lazy.UrlbarPrefs.getScotchBonnetPref(
"untrimOnUserInteraction.featureGate"
) ||
!this._protocolIsTrimmed ||
!this.focused ||
this.#allTextSelected
@ -3663,7 +3666,13 @@ export class UrlbarInput {
// pageproxystate. In order to only show the search icon, switch to
// an invalid pageproxystate.
if (this.window.gBrowser.selectedBrowser.searchTerms) {
this.setPageProxyState("invalid", true);
// When focusing via mousedown, we don't want to cause a shift of the
// string, thus we postpone to the mouseup event.
if (this.focusedViaMousedown) {
this.#setProxyStateToInvalidOnMouseUp = true;
} else {
this.setPageProxyState("invalid", true);
}
}
// If the value was trimmed, check whether we should untrim it.
@ -3677,7 +3686,7 @@ export class UrlbarInput {
try {
let expectedURI = Services.io.newURI(this._untrimmedValue);
if (
lazy.UrlbarPrefs.get("trimHttps") &&
lazy.UrlbarPrefs.getScotchBonnetPref("trimHttps") &&
this._untrimmedValue.startsWith("https://")
) {
untrim =
@ -3728,6 +3737,7 @@ export class UrlbarInput {
_on_mousedown(event) {
switch (event.currentTarget) {
case this.textbox: {
this.toggleAttribute("focusing-via-mousedown", !this.focused);
this._mousedownOnUrlbarDescendant = true;
if (
@ -3807,6 +3817,15 @@ export class UrlbarInput {
}
}
_on_mouseup() {
this.toggleAttribute("focusing-via-mousedown", false);
if (this.#setProxyStateToInvalidOnMouseUp) {
this.#setProxyStateToInvalidOnMouseUp = false;
this.setPageProxyState("invalid", true);
}
}
_on_input(event) {
if (
this._autofillPlaceholder &&
@ -4414,6 +4433,8 @@ export class UrlbarInput {
this._isKeyDownWithMetaAndLeft)
);
}
#setProxyStateToInvalidOnMouseUp = false;
}
/**

View file

@ -189,11 +189,6 @@ const PREF_URLBAR_DEFAULTS = new Map([
// for Pocket suggestions.
["pocket.showLessFrequentlyCount", 0],
// The group-relative suggestedIndex of Pocket suggestions within the Firefox
// Suggest section. This is a fallback pref for the `pocketSuggestIndex` Nimbus
// variable.
["pocket.suggestedIndex", 0],
// If disabled, QuickActions will not be included in either the default search
// mode or the QuickActions search mode.
["quickactions.enabled", true],
@ -539,6 +534,7 @@ const NIMBUS_DEFAULTS = {
experimentType: "",
fakespotMinKeywordLength: 0,
pocketShowLessFrequentlyCap: 0,
pocketSuggestIndex: null,
quickSuggestRemoteSettingsDataType: "data",
quickSuggestScoreMap: null,
recordNavigationalSuggestionTelemetry: false,

View file

@ -847,7 +847,7 @@ class ProviderAutofill extends UrlbarProvider {
if (title) {
payload.title = [title, UrlbarUtils.HIGHLIGHT.TYPED];
} else {
let trimHttps = lazy.UrlbarPrefs.get("trimHttps");
let trimHttps = lazy.UrlbarPrefs.getScotchBonnetPref("trimHttps");
let displaySpec = UrlbarUtils.prepareUrlForDisplay(finalCompleteValue, {
trimURL: false,
});

View file

@ -3075,10 +3075,7 @@ export class TaskQueue {
* then a resolved promise is returned.
*/
get emptyPromise() {
if (!this._queue.length) {
return Promise.resolve();
}
return new Promise(resolve => this._emptyCallbacks.push(resolve));
return this.#emptyPromise;
}
/**
@ -3096,27 +3093,56 @@ export class TaskQueue {
*/
queue(callback) {
return new Promise((resolve, reject) => {
this._queue.push({ callback, resolve, reject });
if (this._queue.length == 1) {
this._doNextTask();
this.#queue.push({ callback, resolve, reject });
if (this.#queue.length == 1) {
this.#emptyDeferred = Promise.withResolvers();
this.#emptyPromise = this.#emptyDeferred.promise;
this.#doNextTask();
}
});
}
/**
* Adds a callback function to the task queue that will be called on idle.
*
* @param {Function} callback
* The function to queue.
* @returns {Promise}
* Resolved after the task queue calls and awaits `callback`. It will be
* resolved with the value returned by `callback`. If `callback` throws an
* error, then it will be rejected with the error.
*/
queueIdleCallback(callback) {
return this.queue(async () => {
await new Promise((resolve, reject) => {
ChromeUtils.idleDispatch(async () => {
try {
let value = await callback();
resolve(value);
} catch (error) {
console.error(error);
reject(error);
}
});
});
});
}
/**
* Calls the next function in the task queue and recurses until the queue is
* empty. Once empty, all empty callback functions are called.
*/
async _doNextTask() {
if (!this._queue.length) {
while (this._emptyCallbacks.length) {
let callback = this._emptyCallbacks.shift();
callback();
}
async #doNextTask() {
if (!this.#queue.length) {
this.#emptyDeferred.resolve();
this.#emptyDeferred = null;
return;
}
let { callback, resolve, reject } = this._queue[0];
// Leave the callback in the queue while awaiting it. If we remove it now
// the queue could become empty, and if `queue()` were called while we're
// awaiting the callback, `#doNextTask()` would be re-entered.
let { callback, resolve, reject } = this.#queue[0];
try {
let value = await callback();
resolve(value);
@ -3124,10 +3150,11 @@ export class TaskQueue {
console.error(error);
reject(error);
}
this._queue.shift();
this._doNextTask();
this.#queue.shift();
this.#doNextTask();
}
_queue = [];
_emptyCallbacks = [];
#queue = [];
#emptyDeferred = null;
#emptyPromise = Promise.resolve();
}

View file

@ -5,6 +5,7 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
});
@ -207,10 +208,24 @@ export class BaseFeature {
/**
* Enables or disables the feature according to `shouldEnable` and whether
* quick suggest is enabled. If the feature is already enabled appropriately,
* does nothing.
* quick suggest is enabled. If the feature's enabled status changes,
* `enable()` is called with the new status; otherwise `enable()` is not
* called. If the feature manages any Rust suggestion types that become
* enabled as a result, they will be ingested.
*/
update() {
// Collect the feature's Rust suggestion types that are currently enabled.
// Features can manage multiple types. Some may be enabled while others are
// disabled. As long as one is enabled, the feature itself will be enabled.
let { rustBackend } = lazy.QuickSuggest;
let oldEnabledRustSuggestionTypes =
rustBackend.isEnabled && this.isEnabled
? this.rustSuggestionTypes.filter(type =>
this.isRustSuggestionTypeEnabled(type)
)
: [];
// Update the feature's enabled status.
let enable =
lazy.UrlbarPrefs.get("quickSuggestEnabled") && this.shouldEnable;
if (enable != this.isEnabled) {
@ -218,6 +233,20 @@ export class BaseFeature {
this.enable(enable);
this.#isEnabled = enable;
}
// Ingest all the feature's Rust suggestion types that just became enabled.
// This will be their initial ingests. After that, their ingests will occur
// according to the ingest timer in the backend.
if (rustBackend.isEnabled && enable) {
for (let type of this.rustSuggestionTypes) {
if (
this.isRustSuggestionTypeEnabled(type) &&
!oldEnabledRustSuggestionTypes.includes(type)
) {
rustBackend.ingestSuggestionType(type);
}
}
}
}
#isEnabled = false;

View file

@ -192,9 +192,11 @@ export class PocketSuggestions extends BaseFeature {
};
if (!suggestion.is_top_pick) {
resultProperties.isSuggestedIndexRelativeToGroup = true;
resultProperties.suggestedIndex =
lazy.UrlbarPrefs.get("pocketSuggestIndex");
let suggestedIndex = lazy.UrlbarPrefs.get("pocketSuggestIndex");
if (suggestedIndex !== null) {
resultProperties.isSuggestedIndexRelativeToGroup = true;
resultProperties.suggestedIndex = suggestedIndex;
}
}
return Object.assign(

View file

@ -18,6 +18,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
Suggestion: "resource://gre/modules/RustSuggest.sys.mjs",
SuggestionProvider: "resource://gre/modules/RustSuggest.sys.mjs",
SuggestionQuery: "resource://gre/modules/RustSuggest.sys.mjs",
TaskQueue: "resource:///modules/UrlbarUtils.sys.mjs",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
Utils: "resource://services-settings/Utils.sys.mjs",
});
@ -31,6 +32,8 @@ XPCOMUtils.defineLazyServiceGetter(
const SUGGEST_DATA_STORE_BASENAME = "suggest.sqlite";
const SPONSORED_SUGGESTION_TYPES = new Set(["Amp", "Fakespot", "Yelp"]);
// This ID is used to register our ingest timer with nsIUpdateTimerManager.
const INGEST_TIMER_ID = "suggest-ingest";
const INGEST_TIMER_LAST_UPDATE_PREF = `app.update.lastUpdateTime.${INGEST_TIMER_ID}`;
@ -72,6 +75,15 @@ const gSuggestionTypesByCtor = new WeakMap();
* [6] https://searchfox.org/mozilla-central/source/toolkit/components/uniffi-bindgen-gecko-js/config.toml
*/
export class SuggestBackendRust extends BaseFeature {
constructor(...args) {
super(...args);
this.#ingestQueue = new lazy.TaskQueue();
this.#setRemoteSettingsConfig({
serverUrl: lazy.Utils.SERVER_URL,
bucketName: lazy.Utils.actualBucketName("main"),
});
}
/**
* @returns {object}
* The global Suggest config from the Rust component as returned from
@ -83,11 +95,10 @@ export class SuggestBackendRust extends BaseFeature {
/**
* @returns {Promise}
* If ingest is pending this will be resolved when it's done. Otherwise it
* was resolved when the previous ingest finished.
* Resolved when all pending ingests are done.
*/
get ingestPromise() {
return this.#ingestPromise;
return this.#ingestQueue.emptyPromise;
}
get shouldEnable() {
@ -103,14 +114,12 @@ export class SuggestBackendRust extends BaseFeature {
}
async query(searchString) {
this.logger.info("Handling query: " + JSON.stringify(searchString));
if (!this.#store) {
// There must have been an error creating `#store`.
this.logger.info("#store is null, returning");
return [];
}
this.logger.debug("Handling query: " + JSON.stringify(searchString));
// Build the list of enabled Rust providers to query.
let providers = this.#rustProviders.reduce(
(memo, { type, feature, provider }) => {
@ -137,7 +146,7 @@ export class SuggestBackendRust extends BaseFeature {
suggestion.source = "rust";
suggestion.provider = type;
suggestion.is_sponsored = type == "Amp" || type == "Yelp";
suggestion.is_sponsored = SPONSORED_SUGGESTION_TYPES.has(type);
if (Array.isArray(suggestion.icon)) {
suggestion.icon_blob = new Blob([new Uint8Array(suggestion.icon)], {
type: suggestion.iconMimetype ?? "",
@ -173,12 +182,66 @@ export class SuggestBackendRust extends BaseFeature {
return this.#configsBySuggestionType.get(type);
}
/**
* Ingests the given suggestion type.
*
* @param {string} type
* A Rust suggestion type name as defined in `suggest.udl`, e.g., "Amp",
* "Wikipedia", "Mdn", etc. See also `BaseFeature.rustSuggestionTypes`.
*/
ingestSuggestionType(type) {
this.#ingestQueue.queueIdleCallback(async () => {
if (!this.#store) {
return;
}
let provider = this.#providerFromSuggestionType(type);
if (!provider) {
return;
}
let timerId;
this.logger.debug("Starting ingest: " + type);
try {
timerId = Glean.urlbar.quickSuggestIngestTime.start();
await this.#store.ingest(
new lazy.SuggestIngestionConstraints({
providers: [provider],
})
);
Glean.urlbar.quickSuggestIngestTime.stopAndAccumulate(timerId);
} catch (error) {
// Ingest can throw a `SuggestApiError` subclass called `Other` with a
// `reason` message, which is very helpful for diagnosing problems with
// remote settings data in tests in particular.
this.logger.error(
`Ingest error for ${type}: ` + (error.reason ?? error)
);
Glean.urlbar.quickSuggestIngestTime.cancel(timerId);
}
this.logger.debug("Finished ingest: " + type);
if (!this.#store) {
return;
}
// Fetch the provider config.
this.logger.debug("Fetching provider config: " + type);
let config = await this.#store.fetchProviderConfig(provider);
this.logger.debug(
`Got provider config for ${type}: ` + JSON.stringify(config)
);
this.#configsBySuggestionType.set(type, config);
this.logger.debug("Finished fetching provider config: " + type);
});
}
/**
* nsITimerCallback
*/
notify() {
this.logger.info("Ingest timer fired");
this.#ingest();
this.#ingestAll();
}
get #storeDataPath() {
@ -209,36 +272,19 @@ export class SuggestBackendRust extends BaseFeature {
let items = [];
for (let [type, feature] of lazy.QuickSuggest
.featuresByRustSuggestionType) {
let key = type.toUpperCase();
if (!lazy.SuggestionProvider.hasOwnProperty(key)) {
this.logger.error(`SuggestionProvider["${key}"] is not defined!`);
continue;
let provider = this.#providerFromSuggestionType(type);
if (provider) {
items.push({ type, feature, provider });
}
items.push({ type, feature, provider: lazy.SuggestionProvider[key] });
}
return items;
}
async #init() {
// Important note on schema updates:
//
// The first time the Suggest store is accessed after a schema version
// update, its backing database will be deleted and a new empty database
// will be created. The database will remain empty until we tell the store
// to ingest. If we wait to ingest as usual until our ingest timer fires,
// the store will remain empty for up to 24 hours, which means we won't
// serve any suggestions at all during that time.
//
// Therefore we simply always ingest here in `#init()`. We'll sometimes
// ingest unnecessarily but that's better than the alternative. (As a
// reminder, for users who have Suggest enabled `#init()` is called whenever
// the Rust backend is enabled, including on startup.)
#init() {
// Initialize the store.
this.logger.info(
`Initializing SuggestStore with data path ${this.#storeDataPath}`
);
let builder = lazy.SuggestStoreBuilder.init()
.dataPath(this.#storeDataPath)
.loadExtension(AppConstants.SQLITE_LIBRARY_FILENAME, "sqlite3_fts5_init")
@ -285,8 +331,10 @@ export class SuggestBackendRust extends BaseFeature {
true // skipFirst
);
// Ingest.
await this.#ingest();
// Do an initial ingest for all enabled suggestion types. When a type
// becomes enabled after this point, its `BaseFeature` will update and call
// `ingestSuggestionType()` for it, which will be its initial ingest.
this.#ingestAll();
}
#uninit() {
@ -298,72 +346,50 @@ export class SuggestBackendRust extends BaseFeature {
this.#shutdownBlocker = null;
}
async #ingest() {
let instance = (this.#ingestInstance = {});
await this.#ingestPromise;
if (instance != this.#ingestInstance) {
return;
}
this.#ingestPromise = new Promise(resolve => {
ChromeUtils.idleDispatch(() => this.#ingestHelper().finally(resolve));
});
await this.#ingestPromise;
}
async #ingestHelper() {
if (!this.#store) {
return;
}
this.logger.info("Starting ingest and configs fetch");
// Do the ingest.
let timerId;
this.logger.debug("Starting ingest");
try {
timerId = Glean.urlbar.quickSuggestIngestTime.start();
await this.#store.ingest(new lazy.SuggestIngestionConstraints());
Glean.urlbar.quickSuggestIngestTime.stopAndAccumulate(timerId);
} catch (error) {
// Ingest can throw a `SuggestApiError` subclass called `Other` that has a
// custom `reason` message, which is very helpful for diagnosing problems
// with remote settings data in tests in particular.
this.logger.error("Ingest error: " + (error.reason ?? error));
Glean.urlbar.quickSuggestIngestTime.cancel(timerId);
}
this.logger.debug("Finished ingest");
if (!this.#store) {
this.logger.info("#store became null, returning from ingest");
return;
#ingestAll() {
// Ingest all enabled suggestion types.
for (let { feature, type } of this.#rustProviders) {
if (feature.isEnabled && feature.isRustSuggestionTypeEnabled(type)) {
this.ingestSuggestionType(type);
}
}
// Fetch the global config.
this.logger.debug("Fetching global config");
this.#config = await this.#store.fetchGlobalConfig();
this.logger.debug("Got global config: " + JSON.stringify(this.#config));
this.#ingestQueue.queueIdleCallback(async () => {
if (!this.#store) {
return;
}
this.logger.debug("Fetching global config");
this.#config = await this.#store.fetchGlobalConfig();
this.logger.debug("Got global config: " + JSON.stringify(this.#config));
});
}
if (!this.#store) {
this.logger.info("#store became null, returning from ingest");
return;
/**
* Given a Rust suggestion type, gets the integer value defined on the
* `SuggestionProvider` object in `RustSuggest.sys.mjs` that identifies the
* corresponding provider to Rust.
*
* @param {string} type
* A Rust suggestion type name as defined in `suggest.udl`, e.g., "Amp",
* "Wikipedia", "Mdn", etc. See also `BaseFeature.rustSuggestionTypes`.
* @returns {number}
* An integer value defined on the `SuggestionProvider` object.
*/
#providerFromSuggestionType(type) {
let key = type.toUpperCase();
if (!lazy.SuggestionProvider.hasOwnProperty(key)) {
this.logger.error(`SuggestionProvider["${key}"] is not defined!`);
return null;
}
return lazy.SuggestionProvider[key];
}
// Fetch all provider configs. We do this for all features, even ones that
// are currently disabled, because they may become enabled before the next
// ingest.
this.logger.debug("Fetching provider configs");
await Promise.all(
this.#rustProviders.map(async ({ type, provider }) => {
let config = await this.#store.fetchProviderConfig(provider);
this.logger.debug(
`Got '${type}' provider config: ` + JSON.stringify(config)
);
this.#configsBySuggestionType.set(type, config);
})
#setRemoteSettingsConfig({ serverUrl, bucketName }) {
this.#remoteSettingsServer = new lazy.RemoteSettingsServer.Custom(
serverUrl
);
this.logger.debug("Finished fetching provider configs");
this.logger.info("Finished ingest and configs fetch");
this.#remoteSettingsBucketName = bucketName;
}
get _test_store() {
@ -371,21 +397,19 @@ export class SuggestBackendRust extends BaseFeature {
}
async _test_setRemoteSettingsConfig({ serverUrl, bucketName }) {
this.#remoteSettingsServer = new lazy.RemoteSettingsServer.Custom(
serverUrl
);
this.#remoteSettingsBucketName = bucketName;
this.#setRemoteSettingsConfig({ serverUrl, bucketName });
if (this.isEnabled) {
// Recreate the store and re-ingest.
Services.prefs.clearUserPref(INGEST_TIMER_LAST_UPDATE_PREF);
this.#uninit();
await this.#init();
this.#init();
await this.ingestPromise;
}
}
async _test_ingest() {
await this.#ingest();
this.#ingestAll();
await this.ingestPromise;
}
// The `SuggestStore` instance.
@ -398,13 +422,10 @@ export class SuggestBackendRust extends BaseFeature {
// `SuggestStore.fetchProviderConfig()`.
#configsBySuggestionType = new Map();
#ingestPromise;
#ingestInstance;
#ingestQueue;
#shutdownBlocker;
#remoteSettingsServer = new lazy.RemoteSettingsServer.Custom(
lazy.Utils.SERVER_URL
);
#remoteSettingsBucketName = lazy.Utils.actualBucketName("main");
#remoteSettingsServer;
#remoteSettingsBucketName;
}
/**
@ -440,7 +461,7 @@ function getSuggestionType(suggestion) {
if (type) {
gSuggestionTypesByCtor.set(suggestion.constructor, type);
} else {
this.logger.error(
console.error(
"Unexpected error: Suggestion class not found on `Suggestion`. " +
"Did the Rust component or its JS bindings change? " +
"The suggestion is: " +

View file

@ -11,7 +11,6 @@ import {
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonTestUtils: "resource://testing-common/AddonTestUtils.sys.mjs",
BrowserTestUtils: "resource://testing-common/BrowserTestUtils.sys.mjs",
BrowserUIUtils: "resource:///modules/BrowserUIUtils.sys.mjs",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
@ -1069,7 +1068,7 @@ export var UrlbarTestUtils = {
}
// Also remove emphasis markers if present.
if (lazy.UrlbarPrefs.get("trimHttps")) {
if (lazy.UrlbarPrefs.getScotchBonnetPref("trimHttps")) {
sanitizedURL = sanitizedURL.replace(/^<?https:\/\/>?/, "");
} else {
sanitizedURL = sanitizedURL.replace(/^<?http:\/\/>?/, "");
@ -1086,7 +1085,7 @@ export var UrlbarTestUtils = {
*/
getTrimmedProtocolWithSlashes() {
if (Services.prefs.getBoolPref("browser.urlbar.trimURLs")) {
return Services.prefs.getBoolPref("browser.urlbar.trimHttps")
return lazy.UrlbarPrefs.getScotchBonnetPref("trimHttps")
? "https://"
: "http://"; // eslint-disable-this-line @microsoft/sdl/no-insecure-url
}
@ -1241,16 +1240,6 @@ export var UrlbarTestUtils = {
Cc["@mozilla.org/satchel/form-history-startup;1"]
.getService(Ci.nsIObserver)
.observe(null, "profile-after-change", null);
// This is necessary because UrlbarMuxerUnifiedComplete.sort calls
// Services.search.parseSubmissionURL, so we need engines.
try {
await lazy.AddonTestUtils.promiseStartupManager();
} catch (error) {
if (!error.message.includes("already started")) {
throw error;
}
}
},
/**

View file

@ -599,6 +599,8 @@ support-files = ["moz.png"]
["browser_tabToSearch.js"]
["browser_text_dont_shift_on_mousedown.js"]
["browser_textruns.js"]
["browser_tokenAlias.js"]

View file

@ -51,7 +51,10 @@ async function checkSearchString(searchString, isIpv6) {
// decodeURI is necessary for matching square brackets in IPV6.
let expectedUrl = isIpv6 ? decodeURI(searchUrl) : searchUrl;
if (UrlbarPrefs.get("trimHttps") && expectedUrl.startsWith("https://")) {
if (
UrlbarPrefs.getScotchBonnetPref("trimHttps") &&
expectedUrl.startsWith("https://")
) {
expectedUrl = expectedUrl.slice("https://".length);
}

View file

@ -143,7 +143,9 @@ const tests = [
get selection() {
// When untrimming is enabled, the behavior differs depending on whether
// the selected text can generate a valid URL, so there may be an offset.
let startOffset = UrlbarPrefs.get("untrimOnUserInteraction.featureGate")
let startOffset = UrlbarPrefs.getScotchBonnetPref(
"untrimOnUserInteraction.featureGate"
)
? gURLBar.value.indexOf(this._expectedSelectedText)
: 0;
return [startOffset, startOffset + this._expectedSelectedText.length];

View file

@ -0,0 +1,104 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that focusing the urlbar with mousedown doesn't shift the text until
* mouseup, as that may cause unexpected text selections.
*/
var gDefaultEngine;
add_setup(async function () {
await SearchTestUtils.installSearchExtension(
{
name: "MozSearch",
search_url: "https://www.example.com/",
search_url_get_params: "q={searchTerms}&pc=fake_code",
},
{ setAsDefault: true }
);
gDefaultEngine = Services.search.getEngineByName("MozSearch");
registerCleanupFunction(async function () {
await PlacesUtils.history.clear();
});
});
add_task(async function test_loaded_page() {
await BrowserTestUtils.withNewTab("https://example.com/", async () => {
let originalX = gURLBar.inputField.getBoundingClientRect().x;
let finalX = getElementXBetweenMouseDownAndUp(gURLBar.inputField);
Assert.equal(originalX, finalX, "The input field didn't move");
});
});
add_task(async function test_invalid_proxystate() {
await BrowserTestUtils.withNewTab("https://example.com/", async () => {
gURLBar.value = "modifiedText";
let originalX = gURLBar.inputField.getBoundingClientRect().x;
let finalX = getElementXBetweenMouseDownAndUp(gURLBar.inputField);
Assert.equal(originalX, finalX, "The input field didn't move");
});
});
add_task(async function test_persistedSearchTerms() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.showSearchTerms.featureGate", true]],
});
await BrowserTestUtils.withNewTab("https://example.com/", async browser => {
const searchTerms = "pizza";
let [expectedSearchUrl] = UrlbarUtils.getSearchQueryUrl(
gDefaultEngine,
searchTerms
);
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
browser,
false,
expectedSearchUrl
);
gURLBar.focus();
gURLBar.value = searchTerms;
EventUtils.synthesizeKey("KEY_Enter");
await browserLoadedPromise;
Assert.equal(gURLBar.value, searchTerms, "Search was persisted");
Assert.ok(!gURLBar.focused, "Urlbar is not focused");
let originalX = gURLBar.inputField.getBoundingClientRect().x;
let finalX = getElementXBetweenMouseDownAndUp(gURLBar.inputField);
Assert.equal(originalX, finalX, "The input field didn't move");
});
});
add_task(async function test_dedicatedSearchButton() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.scotchBonnet.enableOverride", true]],
});
await BrowserTestUtils.withNewTab("https://example.com/", async () => {
let originalX = gURLBar.inputField.getBoundingClientRect().x;
let finalX = getElementXBetweenMouseDownAndUp(gURLBar.inputField);
Assert.equal(originalX, finalX, "The input field didn't move");
});
});
function getElementXBetweenMouseDownAndUp(element) {
EventUtils.synthesizeMouse(
gURLBar.inputField,
10,
10,
{ type: "mousedown" },
element.getOwnerGlobal
);
try {
return element.getBoundingClientRect().x;
} finally {
EventUtils.synthesizeMouse(
gURLBar.inputField,
10,
10,
{ type: "mouseup" },
element.getOwnerGlobal
);
}
}

View file

@ -149,9 +149,12 @@ class _QuickSuggestTestUtils {
* Options object
* @param {Array} options.remoteSettingsRecords
* Array of remote settings records. Each item in this array should be a
* realistic remote settings record with some exceptions, e.g.,
* `record.attachment`, if defined, should be the attachment itself and not
* its metadata. For details see `RemoteSettingsServer.addRecords()`.
* realistic remote settings record with some exceptions as noted below.
* For details see `RemoteSettingsServer.addRecords()`.
* - `record.attachment` - Optional. This should be the attachment itself
* and not its metadata. It should be a JSONable object.
* - `record.collection` - Optional. The name of the RS collection that
* the record should be added to. Defaults to "quicksuggest".
* @param {Array} options.merinoSuggestions
* Array of Merino suggestion objects. If given, this function will start
* the mock Merino server and set `quicksuggest.dataCollection.enabled` to
@ -186,6 +189,19 @@ class _QuickSuggestTestUtils {
} = {}) {
prefs.push(["quicksuggest.enabled", true]);
// Make a Map from collection name to the array of records that should be
// added to that collection.
let recordsByCollection = remoteSettingsRecords.reduce((memo, record) => {
let collection = record.collection || "quicksuggest";
let records = memo.get(collection);
if (!records) {
records = [];
memo.set(collection, records);
}
records.push(record);
return memo;
}, new Map());
// Set up the local remote settings server.
this.#log(
"ensureQuickSuggestInit",
@ -194,13 +210,16 @@ class _QuickSuggestTestUtils {
if (!this.#remoteSettingsServer) {
this.#remoteSettingsServer = new lazy.RemoteSettingsServer();
}
await this.#remoteSettingsServer.setRecords({
this.#remoteSettingsServer.removeRecords();
for (let [collection, records] of recordsByCollection.entries()) {
await this.#remoteSettingsServer.addRecords({ collection, records });
}
await this.#remoteSettingsServer.addRecords({
collection: "quicksuggest",
records: [
...remoteSettingsRecords,
{ type: "configuration", configuration: config },
],
records: [{ type: "configuration", configuration: config }],
});
this.#log("ensureQuickSuggestInit", "Starting remote settings server");
await this.#remoteSettingsServer.start();
this.#log("ensureQuickSuggestInit", "Remote settings server started");

View file

@ -23,11 +23,11 @@ export class RemoteSettingsServer {
*
* @param {object} options
* @param {number} options.maxLogLevel
* A log level value as defined by ConsoleInstance. `Info` logs basic info
* on requests and responses like paths and status codes. Pass `Debug` to
* log more info like headers, response bodies, added and removed records.
* A log level value as defined by ConsoleInstance. `Info` logs server start
* and stop. `Debug` logs requests, responses, and added and removed
* records.
*/
constructor({ maxLogLevel = "Error" } = {}) {
constructor({ maxLogLevel = "Info" } = {}) {
this.#log = console.createInstance({
prefix: "RemoteSettingsServer",
maxLogLevel,
@ -116,10 +116,7 @@ export class RemoteSettingsServer {
* JSON'able objects that the server will convert to JSON in responses.
*/
async addRecords({ bucket = "main", collection = "test", records }) {
this.#log.debug(
"Adding records: " +
JSON.stringify({ bucket, collection, records }, null, 2)
);
this.#log.debug("Adding records:", { bucket, collection, records });
this.#lastModified++;
@ -144,10 +141,9 @@ export class RemoteSettingsServer {
allRecords.push(copy);
}
this.#log.debug(
"Done adding records. All records are now: " +
JSON.stringify([...this.#records.entries()], null, 2)
);
this.#log.debug("Done adding records. All records are now:", [
...this.#records.entries(),
]);
}
/**
@ -170,7 +166,7 @@ export class RemoteSettingsServer {
* `{ type: "data", last_modified: 1234 }
*/
removeRecords(filter = null) {
this.#log.debug("Removing records: " + JSON.stringify({ filter }));
this.#log.debug("Removing records", { filter });
this.#lastModified++;
@ -194,10 +190,9 @@ export class RemoteSettingsServer {
}
}
this.#log.debug(
"Done removing records. All records are now: " +
JSON.stringify([...this.#records.entries()], null, 2)
);
this.#log.debug("Done removing records. All records are now:", [
...this.#records.entries(),
]);
}
/**
@ -311,8 +306,8 @@ export class RemoteSettingsServer {
{
spec: "/v1/buckets/$bucket/collections/$collection/changeset",
response: ({ bucket, collection }) => {
let records = this.#getRecords(bucket, collection);
response: ({ bucket, collection }, request) => {
let records = this.#getRecords(bucket, collection, request);
return !records
? lazy.HTTP_404
: {
@ -327,8 +322,8 @@ export class RemoteSettingsServer {
{
spec: "/v1/buckets/$bucket/collections/$collection/records",
response: ({ bucket, collection }) => {
let records = this.#getRecords(bucket, collection);
response: ({ bucket, collection }, request) => {
let records = this.#getRecords(bucket, collection, request);
return !records
? lazy.HTTP_404
: {
@ -444,8 +439,36 @@ export class RemoteSettingsServer {
return match;
}
#getRecords(bucket, collection) {
return this.#records.get(this.#recordsKey(bucket, collection));
#getRecords(bucket, collection, request) {
let records = this.#records.get(this.#recordsKey(bucket, collection));
let params = new URLSearchParams(request.queryString);
let type = params.get("type");
if (type) {
records = records.filter(r => r.type == type);
}
let gtLastModified = params.get("gt_last_modified");
if (gtLastModified) {
records = records.filter(r => r.last_modified > gtLastModified);
}
let since = params.get("_since");
if (since) {
// Example value: "%221368273600004%22"
let match = /^"([0-9]+)"$/.exec(decodeURIComponent(since));
if (match) {
since = parseInt(match[1]);
records = records.filter(r => r.last_modified > since);
}
}
let sort = params.get("_sort");
if (sort == "last_modified") {
records = records.toSorted((a, b) => a.last_modified - b.last_modified);
}
return records;
}
#recordsKey(bucket, collection) {
@ -563,12 +586,9 @@ export class RemoteSettingsServer {
if (request.queryString) {
pathAndQuery += "?" + request.queryString;
}
this.#log.info(
this.#log.debug(
`< HTTP ${request.httpVersion} ${request.method} ${pathAndQuery}`
);
for (let name of request.headers) {
this.#log.debug(`${name}: ${request.getHeader(name.toString())}`);
}
}
/**
@ -579,18 +599,15 @@ export class RemoteSettingsServer {
* The associated request.
* @param {integer} options.status
* The HTTP status code of the response.
* @param {object} options.headers
* @param {object} options._headers
* An object mapping from response header names to values.
* @param {object} options.body
* The response body, if any.
*/
#logResponse({ request, status, headers, body }) {
this.#log.info(`> ${status} ${request.path}`);
for (let [name, value] of Object.entries(headers)) {
this.#log.debug(`${name}: ${value}`);
}
#logResponse({ request, status, _headers, body }) {
this.#log.debug(`> ${status} ${request.path}`);
if (body) {
this.#log.debug("Response body: " + JSON.stringify(body, null, 2));
this.#log.debug("Response body:", body);
}
}

View file

@ -24,59 +24,6 @@ add_setup(async function () {
await QuickSuggestTestUtils.ensureQuickSuggestInit();
});
// Makes sure `QuickSuggest._updateFeatureState()` is called when the
// `browser.urlbar.quicksuggest.enabled` pref is changed.
add_task(async function test_updateFeatureState_pref() {
Assert.ok(
UrlbarPrefs.get("quicksuggest.enabled"),
"Sanity check: quicksuggest.enabled is true by default"
);
let sandbox = sinon.createSandbox();
let spy = sandbox.spy(QuickSuggest, "_updateFeatureState");
UrlbarPrefs.set("quicksuggest.enabled", false);
Assert.equal(
spy.callCount,
1,
"_updateFeatureState called once after changing pref"
);
UrlbarPrefs.clear("quicksuggest.enabled");
Assert.equal(
spy.callCount,
2,
"_updateFeatureState called again after clearing pref"
);
sandbox.restore();
});
// Makes sure `QuickSuggest._updateFeatureState()` is called when a Nimbus
// experiment is installed and uninstalled.
add_task(async function test_updateFeatureState_experiment() {
let sandbox = sinon.createSandbox();
let spy = sandbox.spy(QuickSuggest, "_updateFeatureState");
await QuickSuggestTestUtils.withExperiment({
callback: () => {
Assert.equal(
spy.callCount,
1,
"_updateFeatureState called once after installing experiment"
);
},
});
Assert.equal(
spy.callCount,
2,
"_updateFeatureState called again after uninstalling experiment"
);
sandbox.restore();
});
add_task(async function test_indexes() {
await QuickSuggestTestUtils.withExperiment({
valueOverrides: {

View file

@ -5,74 +5,66 @@
// Test for Fakespot suggestions.
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
sinon: "resource://testing-common/Sinon.sys.mjs",
});
// TODO: Replace with proper remote settings data once the Fakespot Rust feature
// is vendored in to mozilla-central.
const DUMMY_RESULT = [
const REMOTE_SETTINGS_RECORDS = [
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/maybe-good-item",
title: "Maybe Good Item",
rating: 4.8,
totalReviews: 1234567,
fakespotGrade: "A",
productId: "amazon-0",
},
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/1-review-item",
title: "1 review item",
rating: 5,
totalReviews: 1,
fakespotGrade: "A",
productId: "amazon-1",
},
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/2-reviews-item",
title: "2 reviews item",
rating: 4,
totalReviews: 2,
fakespotGrade: "A",
productId: "amazon-2",
},
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/1000-reviews-item",
title: "1000 reviews item",
rating: 3,
totalReviews: 1000,
fakespotGrade: "A",
productId: "amazon-3",
},
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/99999-reviews-item",
title: "99999 reviews item",
rating: 2,
totalReviews: 99999,
fakespotGrade: "A",
productId: "amazon-4",
},
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/100000-reviews-item",
title: "100000 reviews item",
rating: 1,
totalReviews: 100000,
fakespotGrade: "A",
productId: "amazon-5",
collection: "fakespot-suggest-products",
type: "fakespot-suggestions",
attachment: [
{
url: "https://example.com/maybe-good-item",
title: "Maybe Good Item",
rating: 4.8,
total_reviews: 1234567,
fakespot_grade: "A",
product_id: "amazon-0",
score: 0.01,
},
{
url: "https://example.com/1-review-item",
title: "1 review item",
rating: 5,
total_reviews: 1,
fakespot_grade: "A",
product_id: "amazon-1",
score: 0.1,
},
{
url: "https://example.com/2-reviews-item",
title: "2 reviews item",
rating: 4,
total_reviews: 2,
fakespot_grade: "A",
product_id: "amazon-2",
score: 0.2,
},
{
url: "https://example.com/1000-reviews-item",
title: "1000 reviews item",
rating: 3,
total_reviews: 1000,
fakespot_grade: "A",
product_id: "amazon-3",
score: 0.3,
},
{
url: "https://example.com/99999-reviews-item",
title: "99999 reviews item",
rating: 2,
total_reviews: 99999,
fakespot_grade: "A",
product_id: "amazon-4",
score: 0.4,
},
{
url: "https://example.com/100000-reviews-item",
title: "100000 reviews item",
rating: 1,
total_reviews: 100000,
fakespot_grade: "A",
product_id: "amazon-5",
score: 0.5,
},
],
},
];
@ -81,28 +73,21 @@ const HELP_URL =
"awesome-bar-result-menu";
add_setup(async function () {
const sandbox = lazy.sinon.createSandbox();
sandbox
.stub(lazy.QuickSuggest.rustBackend, "query")
.callsFake(async searchString =>
DUMMY_RESULT.filter(r =>
r.title.toLowerCase().startsWith(searchString.toLowerCase())
)
);
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.quicksuggest.enabled", true],
["browser.urlbar.quicksuggest.rustEnabled", true],
["browser.urlbar.suggest.quicksuggest.sponsored", true],
["browser.urlbar.fakespot.featureGate", true],
["browser.urlbar.suggest.fakespot", true],
set: [["browser.search.suggest.enabled", false]],
});
await QuickSuggestTestUtils.ensureQuickSuggestInit({
remoteSettingsRecords: REMOTE_SETTINGS_RECORDS,
prefs: [
["suggest.quicksuggest.sponsored", true],
["suggest.fakespot", true],
["fakespot.featureGate", true],
],
});
registerCleanupFunction(async () => {
await PlacesUtils.history.clear();
sandbox.restore();
});
});

View file

@ -12,9 +12,6 @@ const REMOTE_SETTINGS_RECORDS = [
},
];
// This stupid test can time out in verify mode on Treeherder Mac machines.
requestLongerTimeout(5);
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [["browser.search.suggest.enabled", false]],
@ -49,9 +46,17 @@ add_task(async function successfulIngest() {
"number",
"newValue.count should be a number"
);
let enabledCount = 0;
for (let [type, feature] of QuickSuggest.featuresByRustSuggestionType) {
if (feature.isEnabled && feature.isRustSuggestionTypeEnabled(type)) {
enabledCount++;
}
}
Assert.equal(
newValue.count,
oldValue.count + 1,
"One new value should have been recorded after ingest"
oldValue.count + enabledCount,
"One new value per enabled suggestion type should have been recorded after ingest"
);
});

View file

@ -8,9 +8,18 @@
ChromeUtils.defineESModuleGetters(this, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
AddonTestUtils: "resource://testing-common/AddonTestUtils.sys.mjs",
ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.sys.mjs",
});
AddonTestUtils.init(this, false);
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"42",
"42"
);
// TODO: Firefox no longer uses `rating` and `number_of_ratings` but they are
// still present in Merino and RS suggestions, so they are included here for
// greater accuracy. We should remove them from Merino, RS, and tests.
@ -85,6 +94,8 @@ const REMOTE_SETTINGS_RESULTS = [
];
add_setup(async function init() {
await AddonTestUtils.promiseStartupManager();
// Disable search suggestions so we don't hit the network.
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);

View file

@ -6,55 +6,117 @@
"use strict";
// TODO: Replace with proper remote settings data once the Fakespot Rust feature
// is vendored in to mozilla-central.
const DUMMY_SUGGESTIONS = [
const REMOTE_SETTINGS_RECORDS = [
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/fakespot-0",
title: "Example Fakespot suggestion",
rating: 4.6,
fakespotGrade: "A",
totalReviews: 167,
productId: "amazon-0",
score: 0.68416834,
is_sponsored: true,
},
{
source: "rust",
provider: "Fakespot",
url: "https://example.com/fakespot-1",
title: "Another Fakespot suggestion",
rating: 3.5,
fakespotGrade: "B",
totalReviews: 100,
productId: "amazon-1",
score: 0.5,
is_sponsored: true,
collection: "fakespot-suggest-products",
type: "fakespot-suggestions",
attachment: [
{
url: "https://example.com/fakespot-0",
title: "Example Fakespot suggestion",
rating: 4.6,
fakespot_grade: "A",
total_reviews: 167,
product_id: "amazon-0",
score: 0.68416834,
},
{
url: "https://example.com/fakespot-1",
title: "Another Fakespot suggestion",
rating: 3.5,
fakespot_grade: "B",
total_reviews: 100,
product_id: "amazon-1",
score: 0.5,
},
],
},
];
const PRIMARY_SEARCH_STRING = "example";
const PRIMARY_TITLE = DUMMY_SUGGESTIONS[0].title;
const PRIMARY_URL = DUMMY_SUGGESTIONS[0].url;
const PRIMARY_TITLE = REMOTE_SETTINGS_RECORDS[0].attachment[0].title;
const PRIMARY_URL = REMOTE_SETTINGS_RECORDS[0].attachment[0].url;
add_setup(async function () {
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
await QuickSuggestTestUtils.ensureQuickSuggestInit({
remoteSettingsRecords: [],
remoteSettingsRecords: REMOTE_SETTINGS_RECORDS,
prefs: [
["suggest.quicksuggest.sponsored", true],
["suggest.fakespot", true],
["fakespot.featureGate", true],
],
});
});
let sandbox = sinon.createSandbox();
sandbox
.stub(QuickSuggest.rustBackend, "query")
.callsFake(async () => DUMMY_SUGGESTIONS);
add_task(async function basic() {
const TEST_DATA = [
{
description: "Basic",
query: "example fakespot suggestion",
expected: {
url: PRIMARY_URL,
title: PRIMARY_TITLE,
},
},
{
description: "With upper case",
query: "ExAmPlE fAKeSpOt SuGgEsTiOn",
expected: {
url: PRIMARY_URL,
title: PRIMARY_TITLE,
},
},
{
description: "Prefix match 1",
query: "ex",
expected: null,
},
{
description: "Prefix match 2",
query: "examp",
expected: {
url: PRIMARY_URL,
title: PRIMARY_TITLE,
},
},
{
description: "First full word",
query: "example",
expected: {
url: PRIMARY_URL,
title: PRIMARY_TITLE,
},
},
{
description: "First full word + prefix",
query: "example f",
expected: {
url: PRIMARY_URL,
title: PRIMARY_TITLE,
},
},
];
for (let { description, query, expected } of TEST_DATA) {
info(
"Doing basic subtest: " +
JSON.stringify({
description,
query,
expected,
})
);
await check_results({
context: createContext(query, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: expected ? [makeExpectedResult(expected)] : [],
});
}
});
add_task(async function telemetryType() {
@ -255,11 +317,11 @@ add_task(async function notRelevant() {
}),
matches: [
makeExpectedResult({
url: DUMMY_SUGGESTIONS[1].url,
title: DUMMY_SUGGESTIONS[1].title,
rating: DUMMY_SUGGESTIONS[1].rating,
totalReviews: DUMMY_SUGGESTIONS[1].totalReviews,
fakespotGrade: DUMMY_SUGGESTIONS[1].fakespotGrade,
url: REMOTE_SETTINGS_RECORDS[0].attachment[1].url,
title: REMOTE_SETTINGS_RECORDS[0].attachment[1].title,
rating: REMOTE_SETTINGS_RECORDS[0].attachment[1].rating,
totalReviews: REMOTE_SETTINGS_RECORDS[0].attachment[1].total_reviews,
fakespotGrade: REMOTE_SETTINGS_RECORDS[0].attachment[1].fakespot_grade,
}),
],
});
@ -442,6 +504,31 @@ add_task(async function showLessFrequently() {
UrlbarPrefs.clear("fakespot.minKeywordLength");
});
// The `Fakespot` Rust provider should be passed to the Rust component when
// querying depending on whether Fakespot suggestions are enabled.
add_task(async function rustProviders() {
await doRustProvidersTests({
searchString: PRIMARY_SEARCH_STRING,
tests: [
{
prefs: {
"suggest.fakespot": true,
},
expectedUrls: [PRIMARY_URL],
},
{
prefs: {
"suggest.fakespot": false,
},
expectedUrls: [],
},
],
});
UrlbarPrefs.clear("suggest.fakespot");
await QuickSuggestTestUtils.forceSync();
});
add_task(async function minKeywordLength_noPrefValue() {
await doMinKeywordLengthTest({
// expected min length: 5 (Nimbus value)
@ -601,26 +688,20 @@ add_task(async function minKeywordLength_onlyPrefValue() {
});
// When no min length is defined in Nimbus or the pref, we should fall back to
// the hardcoded value of 2.
// the natural threshold in the Rust component of 4.
add_task(async function minKeywordLength_noNimbusOrPrefValue() {
await doMinKeywordLengthTest({
// expected min length: 2 (hardcoded)
// expected min length: 4 (in Rust component)
prefValue: null,
nimbusValue: null,
tests: [
{
query: "ex",
expected: {
url: PRIMARY_URL,
title: PRIMARY_TITLE,
},
expected: null,
},
{
query: "exa",
expected: {
url: PRIMARY_URL,
title: PRIMARY_TITLE,
},
expected: null,
},
{
query: "exam",
@ -689,9 +770,9 @@ function makeExpectedResult({
originalUrl = undefined,
displayUrl = undefined,
telemetryType = "fakespot_amazon",
rating = DUMMY_SUGGESTIONS[0].rating,
totalReviews = DUMMY_SUGGESTIONS[0].totalReviews,
fakespotGrade = DUMMY_SUGGESTIONS[0].fakespotGrade,
rating = REMOTE_SETTINGS_RECORDS[0].attachment[0].rating,
totalReviews = REMOTE_SETTINGS_RECORDS[0].attachment[0].total_reviews,
fakespotGrade = REMOTE_SETTINGS_RECORDS[0].attachment[0].fakespot_grade,
} = {}) {
originalUrl ??= url;
@ -723,6 +804,7 @@ function makeExpectedResult({
fakespotGrade,
shouldNavigate: true,
dynamicType: "fakespot",
icon: null,
},
};
}

View file

@ -50,7 +50,7 @@ add_setup(async () => {
// group-relative suggestedIndex. The default Pocket suggestedIndex is 0.
add_task(async function nimbusSuggestedIndex() {
const cleanUpNimbusEnable = await UrlbarTestUtils.initNimbusFeature({
pocketSuggestIndex: -1,
pocketSuggestIndex: 0,
});
await QuickSuggestTestUtils.forceSync();
@ -62,7 +62,7 @@ add_task(async function nimbusSuggestedIndex() {
matches: [
makeExpectedResult({
searchString: LOW_KEYWORD,
suggestedIndex: -1,
suggestedIndex: 0,
}),
],
});
@ -91,7 +91,7 @@ add_task(async function nimbusSuggestedIndex() {
matches: [
makeExpectedResult({
searchString: LOW_KEYWORD,
suggestedIndex: 0,
suggestedIndex: -1,
}),
],
});
@ -551,7 +551,7 @@ function makeExpectedResult({
url.searchParams.set("utm_campaign", "pocket-collections-in-the-address-bar");
url.searchParams.set("utm_content", "treatment");
let expectedSuggestedIndex = 0;
let expectedSuggestedIndex = -1;
if (suggestedIndex !== undefined) {
expectedSuggestedIndex = suggestedIndex;
} else if (isTopPick) {

View file

@ -9,6 +9,18 @@
"use strict";
ChromeUtils.defineESModuleGetters(this, {
AddonTestUtils: "resource://testing-common/AddonTestUtils.sys.mjs",
});
AddonTestUtils.init(this, false);
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"42",
"42"
);
const { DEFAULT_SUGGESTION_SCORE } = UrlbarProviderQuickSuggest;
const REMOTE_SETTINGS_RECORDS = [
@ -131,6 +143,8 @@ const MERINO_UNKNOWN_SUGGESTION = {
};
add_setup(async function init() {
await AddonTestUtils.promiseStartupManager();
// Disable search suggestions so we don't hit the network.
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);

View file

@ -67,7 +67,7 @@ add_task(async function interval() {
// Re-enable the backend with a small ingest interval. A new ingest will
// immediately start.
let intervalSecs = 1;
let intervalSecs = 3;
UrlbarPrefs.set("quicksuggest.rustIngestIntervalSeconds", intervalSecs);
UrlbarPrefs.set("quicksuggest.rustEnabled", false);
UrlbarPrefs.set("quicksuggest.rustEnabled", true);
@ -99,7 +99,7 @@ add_task(async function interval() {
);
// Wait for ingest to start and finish.
info("Waiting for ingest to start at index " + i);
info(`Waiting ${intervalSecs}s for ingest to start at index ${i}`);
({ ingestPromise } = await waitForIngestStart(ingestPromise));
info("Waiting for ingest to finish at index " + i);
await ingestPromise;
@ -118,7 +118,7 @@ add_task(async function interval() {
// We'll simply wait for a few seconds up to two times until no new ingests
// start.
let waitSecs = 3 * intervalSecs;
let waitSecs = 2 * intervalSecs;
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
let wait = () => new Promise(r => setTimeout(r, 1000 * waitSecs));

View file

@ -12,7 +12,6 @@ var { UrlbarMuxer, UrlbarProvider, UrlbarQueryContext, UrlbarUtils } =
ChromeUtils.importESModule("resource:///modules/UrlbarUtils.sys.mjs");
ChromeUtils.defineESModuleGetters(this, {
AddonTestUtils: "resource://testing-common/AddonTestUtils.sys.mjs",
HttpServer: "resource://testing-common/httpd.sys.mjs",
PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
@ -58,14 +57,10 @@ ChromeUtils.defineLazyGetter(this, "PlacesFrecencyRecalculator", () => {
).wrappedJSObject;
});
// Most tests need a profile to be setup, so do that here.
do_get_profile();
SearchTestUtils.init(this);
AddonTestUtils.init(this, false);
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"42",
"42"
);
const SUGGESTIONS_ENGINE_NAME = "Suggestions";
const TAIL_SUGGESTIONS_ENGINE_NAME = "Tail Suggestions";

View file

@ -16,6 +16,9 @@ const fieldTemplates = {
name: item.fieldId,
required: item.required,
value: item.value ?? "",
// Conditionally add pattern attribute since pattern=""/false/undefined
// results in weird behaviour.
...(item.pattern && { pattern: item.pattern }),
};
},
input(item) {

View file

@ -49,15 +49,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -22,14 +22,18 @@ class PictureInPictureVideoWrapper {
};
callback([1], null);
let captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver = new MutationObserver(callback);
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -17,15 +17,19 @@ class PictureInPictureVideoWrapper {
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -42,15 +42,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -28,15 +28,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -28,8 +28,8 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback();
let captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver = new MutationObserver(callback);
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
@ -56,15 +56,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -19,15 +19,19 @@ class PictureInPictureVideoWrapper {
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: true,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -8,9 +8,11 @@ class PictureInPictureVideoWrapper {
setVolume(video, volume) {
video.volume = volume;
}
isMuted(video) {
return video.volume === 0;
}
setMuted(video, shouldMute) {
if (shouldMute) {
this.setVolume(video, 0);
@ -18,6 +20,7 @@ class PictureInPictureVideoWrapper {
this.setVolume(video, 1);
}
}
setCaptionContainerObserver(video, updateCaptionsFunction) {
let container = document.querySelector(
'[data-testid="CueBoxContainer"]'
@ -34,15 +37,19 @@ class PictureInPictureVideoWrapper {
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -25,15 +25,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -8,15 +8,19 @@ class PictureInPictureVideoWrapper {
constructor(video) {
this.player = video.wrappedJSObject.__HuluDashPlayer__;
}
play() {
this.player.play();
}
pause() {
this.player.pause();
}
isMuted(video) {
return video.volume === 0;
}
setMuted(video, shouldMute) {
let muteButton = document.querySelector(".VolumeControl > div");
@ -24,9 +28,11 @@ class PictureInPictureVideoWrapper {
muteButton.click();
}
}
setCurrentTime(video, position) {
this.player.currentTime = position;
}
setCaptionContainerObserver(video, updateCaptionsFunction) {
let container = document.querySelector(".ClosedCaption");
@ -54,15 +60,20 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
getDuration() {
return this.player.duration;
}

View file

@ -25,15 +25,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback();
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -18,6 +18,7 @@ class PictureInPictureVideoWrapper {
}
this.player = netflixPlayerAPI.getVideoPlayerBySessionId(sessionId);
}
/**
* The Netflix player returns the current time in milliseconds so we convert
* to seconds before returning.
@ -27,6 +28,7 @@ class PictureInPictureVideoWrapper {
getCurrentTime() {
return this.player.getCurrentTime() / 1000;
}
/**
* The Netflix player returns the duration in milliseconds so we convert to
* seconds before returning.
@ -36,9 +38,11 @@ class PictureInPictureVideoWrapper {
getDuration() {
return this.player.getDuration() / 1000;
}
play() {
this.player.play();
}
pause() {
this.player.pause();
}
@ -56,9 +60,9 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
@ -66,6 +70,10 @@ class PictureInPictureVideoWrapper {
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
/**
* Set the current time of the video in milliseconds.
*
@ -75,15 +83,19 @@ class PictureInPictureVideoWrapper {
setCurrentTime(video, position) {
this.player.seek(position * 1000);
}
setVolume(video, volume) {
this.player.setVolume(volume);
}
getVolume() {
return this.player.getVolume();
}
setMuted(video, shouldMute) {
this.player.setMuted(shouldMute);
}
isMuted() {
return this.player.isMuted();
}

View file

@ -23,15 +23,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -27,15 +27,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -18,6 +18,7 @@ class PictureInPictureVideoWrapper {
video.play();
});
}
/**
* Seeking large amounts of time can cause the video readyState to
* HAVE_METADATA (1) and it will throw an error when trying to play the video.
@ -52,6 +53,7 @@ class PictureInPictureVideoWrapper {
});
}
}
setCaptionContainerObserver(video, updateCaptionsFunction) {
let container = document?.querySelector("#dv-web-player");
@ -85,9 +87,9 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: true,
childList: true,
subtree: true,
@ -95,6 +97,10 @@ class PictureInPictureVideoWrapper {
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
shouldHideToggle(video) {
return !!video.classList.contains("tst-video-overlay-player-html5");
}

View file

@ -25,15 +25,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -21,15 +21,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: true,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -21,15 +21,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: true,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -8,6 +8,7 @@ class PictureInPictureVideoWrapper {
isLive() {
return !document.querySelector(".seekbar-bar");
}
getDuration(video) {
if (this.isLive(video)) {
return Infinity;

View file

@ -52,15 +52,18 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: true,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -24,15 +24,19 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -23,15 +23,18 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -28,15 +28,19 @@ class PictureInPictureVideoWrapper {
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View file

@ -14,9 +14,11 @@ class PictureInPictureVideoWrapper {
? shortsPlayer
: video.closest("#movie_player")?.wrappedJSObject;
}
isLive() {
return !!document.querySelector(".ytp-live");
}
setMuted(video, shouldMute) {
if (this.player) {
if (shouldMute) {
@ -28,12 +30,14 @@ class PictureInPictureVideoWrapper {
video.muted = shouldMute;
}
}
getDuration(video) {
if (this.isLive(video)) {
return Infinity;
}
return video.duration;
}
setCaptionContainerObserver(video, updateCaptionsFunction) {
let container = document.getElementById("ytp-caption-window-container");
@ -59,18 +63,24 @@ class PictureInPictureVideoWrapper {
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
this.captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
this.captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
removeCaptionContainerObserver() {
this.captionsObserver?.disconnect();
}
shouldHideToggle(video) {
return !!video.closest(".ytd-video-preview");
}
setVolume(video, volume) {
if (this.player) {
this.player.setVolume(volume * 100);
@ -78,6 +88,7 @@ class PictureInPictureVideoWrapper {
video.volume = volume;
}
}
getVolume(video) {
if (this.player) {
return this.player.getVolume() / 100;

View file

@ -1210,6 +1210,23 @@ const AVAILABLE_UA_OVERRIDES = [
},
},
},
{
/*
* Bug 1842767 - UA override for passport.bilibili.com
*
* Spoofing as Chrome makes the login page use a mobile layout.
*/
id: "bug1842767",
platform: "android",
domain: "passport.bilibili.com",
bug: "1842767",
config: {
matches: ["*://*.passport.bilibili.com/*"],
uaTransformer: () => {
return UAHelpers.getDeviceAppropriateChromeUA();
},
},
},
];
module.exports = AVAILABLE_UA_OVERRIDES;

View file

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Web Compatibility Interventions",
"description": "Urgent post-release fixes for web compatibility.",
"version": "129.4.0",
"version": "129.5.0",
"browser_specific_settings": {
"gecko": {
"id": "webcompat@mozilla.org",

View file

@ -16,7 +16,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"af": {
"pin": false,
@ -35,7 +35,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"an": {
"pin": false,
@ -54,7 +54,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ar": {
"pin": false,
@ -73,7 +73,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ast": {
"pin": false,
@ -92,7 +92,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"az": {
"pin": false,
@ -111,7 +111,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"be": {
"pin": false,
@ -130,7 +130,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"bg": {
"pin": false,
@ -149,7 +149,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"bn": {
"pin": false,
@ -168,7 +168,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"bo": {
"pin": false,
@ -187,7 +187,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"br": {
"pin": false,
@ -206,7 +206,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"brx": {
"pin": false,
@ -225,7 +225,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"bs": {
"pin": false,
@ -244,7 +244,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ca": {
"pin": false,
@ -263,7 +263,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ca-valencia": {
"pin": false,
@ -282,7 +282,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"cak": {
"pin": false,
@ -301,7 +301,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ckb": {
"pin": false,
@ -320,7 +320,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"cs": {
"pin": false,
@ -339,7 +339,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"cy": {
"pin": false,
@ -358,7 +358,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"da": {
"pin": false,
@ -377,7 +377,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"de": {
"pin": false,
@ -396,7 +396,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"dsb": {
"pin": false,
@ -415,7 +415,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"el": {
"pin": false,
@ -434,7 +434,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"en-CA": {
"pin": false,
@ -453,7 +453,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"en-GB": {
"pin": false,
@ -472,7 +472,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"eo": {
"pin": false,
@ -491,7 +491,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"es-AR": {
"pin": false,
@ -510,7 +510,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"es-CL": {
"pin": false,
@ -529,7 +529,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"es-ES": {
"pin": false,
@ -548,7 +548,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"es-MX": {
"pin": false,
@ -567,7 +567,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"et": {
"pin": false,
@ -586,7 +586,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"eu": {
"pin": false,
@ -605,7 +605,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"fa": {
"pin": false,
@ -624,7 +624,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ff": {
"pin": false,
@ -643,7 +643,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"fi": {
"pin": false,
@ -662,7 +662,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"fr": {
"pin": false,
@ -681,7 +681,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"fur": {
"pin": false,
@ -700,7 +700,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"fy-NL": {
"pin": false,
@ -719,7 +719,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ga-IE": {
"pin": false,
@ -738,7 +738,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"gd": {
"pin": false,
@ -757,7 +757,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"gl": {
"pin": false,
@ -776,7 +776,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"gn": {
"pin": false,
@ -795,7 +795,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"gu-IN": {
"pin": false,
@ -814,7 +814,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"he": {
"pin": false,
@ -833,7 +833,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"hi-IN": {
"pin": false,
@ -852,7 +852,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"hr": {
"pin": false,
@ -871,7 +871,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"hsb": {
"pin": false,
@ -890,7 +890,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"hu": {
"pin": false,
@ -909,7 +909,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"hy-AM": {
"pin": false,
@ -928,7 +928,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"hye": {
"pin": false,
@ -947,7 +947,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ia": {
"pin": false,
@ -966,7 +966,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"id": {
"pin": false,
@ -985,7 +985,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"is": {
"pin": false,
@ -1004,7 +1004,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"it": {
"pin": false,
@ -1023,7 +1023,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ja": {
"pin": false,
@ -1040,7 +1040,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ja-JP-mac": {
"pin": false,
@ -1048,7 +1048,7 @@
"macosx64",
"macosx64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ka": {
"pin": false,
@ -1067,7 +1067,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"kab": {
"pin": false,
@ -1086,7 +1086,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"kk": {
"pin": false,
@ -1105,7 +1105,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"km": {
"pin": false,
@ -1124,7 +1124,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"kn": {
"pin": false,
@ -1143,7 +1143,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ko": {
"pin": false,
@ -1162,7 +1162,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"lij": {
"pin": false,
@ -1181,7 +1181,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"lo": {
"pin": false,
@ -1200,7 +1200,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"lt": {
"pin": false,
@ -1219,7 +1219,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ltg": {
"pin": false,
@ -1238,7 +1238,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"lv": {
"pin": false,
@ -1257,7 +1257,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"meh": {
"pin": false,
@ -1276,7 +1276,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"mk": {
"pin": false,
@ -1295,7 +1295,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"mr": {
"pin": false,
@ -1314,7 +1314,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ms": {
"pin": false,
@ -1333,7 +1333,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"my": {
"pin": false,
@ -1352,7 +1352,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"nb-NO": {
"pin": false,
@ -1371,7 +1371,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ne-NP": {
"pin": false,
@ -1390,7 +1390,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"nl": {
"pin": false,
@ -1409,7 +1409,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"nn-NO": {
"pin": false,
@ -1428,7 +1428,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"oc": {
"pin": false,
@ -1447,7 +1447,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"pa-IN": {
"pin": false,
@ -1466,7 +1466,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"pl": {
"pin": false,
@ -1485,7 +1485,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"pt-BR": {
"pin": false,
@ -1504,7 +1504,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"pt-PT": {
"pin": false,
@ -1523,7 +1523,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"rm": {
"pin": false,
@ -1542,7 +1542,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ro": {
"pin": false,
@ -1561,7 +1561,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ru": {
"pin": false,
@ -1580,7 +1580,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sat": {
"pin": false,
@ -1599,7 +1599,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sc": {
"pin": false,
@ -1618,7 +1618,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"scn": {
"pin": false,
@ -1637,7 +1637,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sco": {
"pin": false,
@ -1656,7 +1656,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"si": {
"pin": false,
@ -1675,7 +1675,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sk": {
"pin": false,
@ -1694,7 +1694,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"skr": {
"pin": false,
@ -1713,7 +1713,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sl": {
"pin": false,
@ -1732,7 +1732,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"son": {
"pin": false,
@ -1751,7 +1751,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sq": {
"pin": false,
@ -1770,7 +1770,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sr": {
"pin": false,
@ -1789,7 +1789,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"sv-SE": {
"pin": false,
@ -1808,7 +1808,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"szl": {
"pin": false,
@ -1827,7 +1827,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ta": {
"pin": false,
@ -1846,7 +1846,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"te": {
"pin": false,
@ -1865,7 +1865,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"tg": {
"pin": false,
@ -1884,7 +1884,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"th": {
"pin": false,
@ -1903,7 +1903,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"tl": {
"pin": false,
@ -1922,7 +1922,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"tr": {
"pin": false,
@ -1941,7 +1941,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"trs": {
"pin": false,
@ -1960,7 +1960,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"uk": {
"pin": false,
@ -1979,7 +1979,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"ur": {
"pin": false,
@ -1998,7 +1998,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"uz": {
"pin": false,
@ -2017,7 +2017,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"vi": {
"pin": false,
@ -2036,7 +2036,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"wo": {
"pin": false,
@ -2055,7 +2055,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"xh": {
"pin": false,
@ -2074,7 +2074,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"zh-CN": {
"pin": false,
@ -2093,7 +2093,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
},
"zh-TW": {
"pin": false,
@ -2112,6 +2112,6 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "84033faef5b836dbc50cf65dab575e5e0fc1f7ac"
"revision": "ab8ebcd54e470175eb3890a5f7d560ea67b46640"
}
}

View file

@ -3,15 +3,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { UrlbarPrefs } from "resource:///modules/UrlbarPrefs.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"trimHttps",
"browser.urlbar.trimHttps"
);
export var BrowserUIUtils = {
/**
* Check whether a page can be considered as 'empty', that its URI
@ -149,7 +143,9 @@ export var BrowserUIUtils = {
},
get trimURLProtocol() {
return lazy.trimHttps ? "https://" : "http://";
return UrlbarPrefs.getScotchBonnetPref("trimHttps")
? "https://"
: "http://";
},
/**

View file

@ -1508,3 +1508,16 @@ richlistitem .text-link:hover {
appearance: none;
}
}
.section-heading,
.section-description {
margin: 0 0 var(--space-small);
}
.section-header-last {
margin: 0 0 var(--space-large);
}
.search-header:has(.section-heading) {
margin: 0;
}

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