Update On Thu Sep 14 20:53:30 CEST 2023
This commit is contained in:
parent
dedbbf188a
commit
d8b708e2ec
2325 changed files with 47579 additions and 30132 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -1036,15 +1036,6 @@ dependencies = [
|
|||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cssparser-color"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cssparser-macros"
|
||||
version = "0.6.1"
|
||||
|
@ -5191,7 +5182,6 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"cssparser",
|
||||
"cssparser-color",
|
||||
"derive_more 0.99.999",
|
||||
"dom",
|
||||
"euclid",
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "mozilla/EditorBase.h"
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
#include "mozilla/ipc/ProcessChild.h"
|
||||
#include "mozilla/PerfStats.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "mozilla/a11y/DocAccessibleChild.h"
|
||||
|
@ -1456,6 +1457,9 @@ void DocAccessible::ProcessInvalidationList() {
|
|||
void DocAccessible::ProcessQueuedCacheUpdates() {
|
||||
AUTO_PROFILER_MARKER_TEXT("DocAccessible::ProcessQueuedCacheUpdates", A11Y,
|
||||
{}, ""_ns);
|
||||
PerfStats::AutoMetricRecording<
|
||||
PerfStats::Metric::A11Y_ProcessQueuedCacheUpdate>
|
||||
autoRecording;
|
||||
// DO NOT ADD CODE ABOVE THIS BLOCK: THIS CODE IS MEASURING TIMINGS.
|
||||
|
||||
nsTArray<CacheData> data;
|
||||
|
@ -1563,6 +1567,8 @@ void DocAccessible::NotifyOfLoading(bool aIsReloading) {
|
|||
|
||||
void DocAccessible::DoInitialUpdate() {
|
||||
AUTO_PROFILER_MARKER_TEXT("DocAccessible::DoInitialUpdate", A11Y, {}, ""_ns);
|
||||
PerfStats::AutoMetricRecording<PerfStats::Metric::A11Y_DoInitialUpdate>
|
||||
autoRecording;
|
||||
// DO NOT ADD CODE ABOVE THIS BLOCK: THIS CODE IS MEASURING TIMINGS.
|
||||
|
||||
if (nsCoreUtils::IsTopLevelContentDocInProcess(mDocumentNode)) {
|
||||
|
|
|
@ -357,7 +357,9 @@ using namespace mozilla::a11y;
|
|||
|
||||
TableCellAccessible* cell = mGeckoAccessible->AsTableCell();
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
cell->RowHeaderCells(&headerCells);
|
||||
if (cell) {
|
||||
cell->RowHeaderCells(&headerCells);
|
||||
}
|
||||
return utils::ConvertToNSArray(headerCells);
|
||||
}
|
||||
|
||||
|
@ -366,7 +368,9 @@ using namespace mozilla::a11y;
|
|||
|
||||
TableCellAccessible* cell = mGeckoAccessible->AsTableCell();
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
cell->ColHeaderCells(&headerCells);
|
||||
if (cell) {
|
||||
cell->ColHeaderCells(&headerCells);
|
||||
}
|
||||
return utils::ConvertToNSArray(headerCells);
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,9 @@ static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI;
|
|||
|
||||
static mozilla::nt::SRWLock gLoaderObserverLock;
|
||||
static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver;
|
||||
static CONDITION_VARIABLE gLoaderObserverNoOngoingLoadsCV =
|
||||
CONDITION_VARIABLE_INIT;
|
||||
static mozilla::Atomic<uint32_t> gLoaderObserverOngoingLoadsCount(0);
|
||||
|
||||
namespace mozilla {
|
||||
namespace freestanding {
|
||||
|
@ -177,7 +180,11 @@ ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad(
|
|||
|
||||
bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
|
||||
PHANDLE aOutHandle) {
|
||||
nt::AutoSharedLock lock(gLoaderObserverLock);
|
||||
// This method should only be called between NotifyBeginDllLoad and
|
||||
// NotifyEndDllLoad, so it is already protected against gLoaderObserver
|
||||
// change, through gLoaderObserverOngoingLoadsCount.
|
||||
MOZ_RELEASE_ASSERT(gLoaderObserverOngoingLoadsCount);
|
||||
|
||||
return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle);
|
||||
}
|
||||
|
||||
|
@ -190,13 +197,21 @@ void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext,
|
|||
aModuleLoadInfo.CaptureBacktrace();
|
||||
}
|
||||
|
||||
nt::AutoSharedLock lock(gLoaderObserverLock);
|
||||
// This method should only be called after a matching call to
|
||||
// NotifyBeginDllLoad, so it is already protected against gLoaderObserver
|
||||
// change, through gLoaderObserverOngoingLoadsCount.
|
||||
|
||||
// We need to notify the observer that the DLL load has ended even when
|
||||
// |aLoadNtStatus| indicates a failure. This is to ensure that any resources
|
||||
// acquired by the observer during OnBeginDllLoad are cleaned up.
|
||||
gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus,
|
||||
std::move(aModuleLoadInfo));
|
||||
|
||||
auto previousValue = gLoaderObserverOngoingLoadsCount--;
|
||||
MOZ_RELEASE_ASSERT(previousValue);
|
||||
if (previousValue == 1) {
|
||||
::RtlWakeAllConditionVariable(&gLoaderObserverNoOngoingLoadsCV);
|
||||
}
|
||||
}
|
||||
|
||||
nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName(
|
||||
|
@ -246,6 +261,7 @@ nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer(
|
|||
void LoaderPrivateAPIImp::NotifyBeginDllLoad(
|
||||
void** aContext, PCUNICODE_STRING aRequestedDllName) {
|
||||
nt::AutoSharedLock lock(gLoaderObserverLock);
|
||||
++gLoaderObserverOngoingLoadsCount;
|
||||
gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName);
|
||||
}
|
||||
|
||||
|
@ -260,6 +276,11 @@ void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) {
|
|||
nt::LoaderObserver* prevLoaderObserver = nullptr;
|
||||
|
||||
nt::AutoExclusiveLock lock(gLoaderObserverLock);
|
||||
while (gLoaderObserverOngoingLoadsCount) {
|
||||
NTSTATUS status = ::RtlSleepConditionVariableSRW(
|
||||
&gLoaderObserverNoOngoingLoadsCV, &gLoaderObserverLock, nullptr, 0);
|
||||
MOZ_RELEASE_ASSERT(NT_SUCCESS(status) && status != STATUS_TIMEOUT);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aNewObserver);
|
||||
if (!aNewObserver) {
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[DEFAULT]
|
||||
|
||||
[test_aboutCrashed.xhtml]
|
||||
[test_aboutRestartRequired.xhtml]
|
5
browser/base/content/test/chrome/chrome.toml
Normal file
5
browser/base/content/test/chrome/chrome.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
|
||||
["test_aboutCrashed.xhtml"]
|
||||
|
||||
["test_aboutRestartRequired.xhtml"]
|
|
@ -14,7 +14,7 @@ with Files("content/docs/sslerrorreport/**"):
|
|||
SCHEDULES.exclusive = ["docs"]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += [
|
||||
"content/test/chrome/chrome.ini",
|
||||
"content/test/chrome/chrome.toml",
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
|
|
|
@ -3479,7 +3479,19 @@ BrowserGlue.prototype = {
|
|||
if (bookmarksUrl) {
|
||||
// Import from bookmarks.html file.
|
||||
try {
|
||||
if (Services.policies.isAllowed("defaultBookmarks")) {
|
||||
if (
|
||||
Services.policies.isAllowed("defaultBookmarks") &&
|
||||
// Default bookmarks are imported after startup, and they may
|
||||
// influence the outcome of tests, thus it's possible to use
|
||||
// this test-only pref to skip the import.
|
||||
!(
|
||||
Cu.isInAutomation &&
|
||||
Services.prefs.getBoolPref(
|
||||
"browser.bookmarks.testing.skipDefaultBookmarksImport",
|
||||
false
|
||||
)
|
||||
)
|
||||
) {
|
||||
await lazy.BookmarkHTMLUtils.importFromURL(bookmarksUrl, {
|
||||
replace: true,
|
||||
source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
|
||||
|
|
|
@ -19,5 +19,5 @@ FINAL_TARGET_FILES.actors += [
|
|||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.ini"]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.ini"]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.toml"]
|
||||
XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.ini"]
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
[DEFAULT]
|
||||
scheme = https
|
||||
prefs =
|
||||
identity.fxaccounts.enabled=true
|
||||
support-files =
|
||||
aboutlogins_common.js
|
||||
|
||||
[test_confirm_delete_dialog.html]
|
||||
[test_fxaccounts_button.html]
|
||||
[test_login_filter.html]
|
||||
[test_login_item.html]
|
||||
[test_login_list.html]
|
||||
[test_menu_button.html]
|
16
browser/components/aboutlogins/tests/chrome/chrome.toml
Normal file
16
browser/components/aboutlogins/tests/chrome/chrome.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[DEFAULT]
|
||||
scheme = "https"
|
||||
prefs = ["identity.fxaccounts.enabled=true"]
|
||||
support-files = ["aboutlogins_common.js"]
|
||||
|
||||
["test_confirm_delete_dialog.html"]
|
||||
|
||||
["test_fxaccounts_button.html"]
|
||||
|
||||
["test_login_filter.html"]
|
||||
|
||||
["test_login_item.html"]
|
||||
|
||||
["test_login_list.html"]
|
||||
|
||||
["test_menu_button.html"]
|
|
@ -161,6 +161,15 @@ export const DoHController = {
|
|||
lazy.Preferences.observe(NATIVE_FALLBACK_WARNING_HEURISTIC_LIST_PREF, this);
|
||||
|
||||
if (lazy.DoHConfigController.currentConfig.enabled) {
|
||||
// At init time set these heuristics to false if we may run heuristics
|
||||
for (let key of lazy.Heuristics.Telemetry.heuristicNames()) {
|
||||
Services.telemetry.keyedScalarSet(
|
||||
"networking.doh_heuristic_ever_tripped",
|
||||
key,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
await this.maybeEnableHeuristics();
|
||||
} else if (lazy.Preferences.get(FIRST_RUN_PREF, false)) {
|
||||
await this.rollback();
|
||||
|
@ -216,8 +225,32 @@ export const DoHController = {
|
|||
|
||||
let policyResult = await lazy.Heuristics.checkEnterprisePolicy();
|
||||
|
||||
if (["policy_without_doh", "disable_doh"].includes(policyResult)) {
|
||||
await this.setState("policyDisabled");
|
||||
if (policyResult != "no_policy_set") {
|
||||
switch (policyResult) {
|
||||
case "policy_without_doh":
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.enterprisePresent
|
||||
);
|
||||
await this.setState("policyDisabled");
|
||||
break;
|
||||
case "disable_doh":
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.enterpriseDisabled
|
||||
);
|
||||
await this.setState("policyDisabled");
|
||||
break;
|
||||
case "enable_doh":
|
||||
// The TRR mode has already been set, so theoretically we should not get here.
|
||||
// XXX: should we skip heuristics or continue?
|
||||
// TODO: Make sure we use the correct URL if the policy defines one.
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.enterpriseEnabled
|
||||
);
|
||||
break;
|
||||
}
|
||||
lazy.Preferences.set(SKIP_HEURISTICS_PREF, true);
|
||||
return;
|
||||
}
|
||||
|
@ -326,6 +359,11 @@ export const DoHController = {
|
|||
async runHeuristics(evaluateReason) {
|
||||
let start = Date.now();
|
||||
|
||||
Services.telemetry.scalarAdd("networking.doh_heuristics_attempts", 1);
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.incomplete
|
||||
);
|
||||
let results = await lazy.Heuristics.run();
|
||||
|
||||
if (
|
||||
|
@ -339,6 +377,10 @@ export const DoHController = {
|
|||
// during this heuristics run. We simply discard the results in this case.
|
||||
// Same thing if there was another heuristics run triggered or if we have
|
||||
// detected a locked captive portal while this one was ongoing.
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.ignored
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -382,6 +424,11 @@ export const DoHController = {
|
|||
|
||||
this.setHeuristicResult(Ci.nsITRRSkipReason.TRR_UNSET);
|
||||
if (decision === lazy.Heuristics.DISABLE_DOH) {
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.fromResults(results)
|
||||
);
|
||||
|
||||
let fallbackHeuristicTripped = undefined;
|
||||
if (lazy.Preferences.get(NATIVE_FALLBACK_WARNING_PREF, false)) {
|
||||
let heuristics = lazy.Preferences.get(
|
||||
|
@ -411,6 +458,11 @@ export const DoHController = {
|
|||
|
||||
await this.setState("disabled");
|
||||
} else {
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.pass
|
||||
);
|
||||
Services.telemetry.scalarAdd("networking.doh_heuristics_pass_count", 1);
|
||||
await this.setState("enabled");
|
||||
}
|
||||
|
||||
|
@ -440,6 +492,14 @@ export const DoHController = {
|
|||
} else if (["vpn", "proxy", "nrpt"].includes(heuristicName)) {
|
||||
platform.push(heuristicName);
|
||||
}
|
||||
|
||||
if (lazy.Heuristics.Telemetry.heuristicNames().includes(heuristicName)) {
|
||||
Services.telemetry.keyedScalarSet(
|
||||
"networking.doh_heuristic_ever_tripped",
|
||||
heuristicName,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
resultsForTelemetry.canaries = canaries.join(",");
|
||||
|
@ -461,9 +521,6 @@ export const DoHController = {
|
|||
case "disabled":
|
||||
lazy.Preferences.set(ROLLOUT_MODE_PREF, 0);
|
||||
break;
|
||||
case "UIOk":
|
||||
lazy.Preferences.set(BREADCRUMB_PREF, true);
|
||||
break;
|
||||
case "enabled":
|
||||
lazy.Preferences.set(ROLLOUT_MODE_PREF, 2);
|
||||
lazy.Preferences.set(BREADCRUMB_PREF, true);
|
||||
|
@ -491,6 +548,32 @@ export const DoHController = {
|
|||
state,
|
||||
"null"
|
||||
);
|
||||
|
||||
let modePref = lazy.Preferences.get(NETWORK_TRR_MODE_PREF);
|
||||
if (state == "manuallyDisabled") {
|
||||
if (
|
||||
modePref == Ci.nsIDNSService.MODE_TRRFIRST ||
|
||||
modePref == Ci.nsIDNSService.MODE_TRRONLY
|
||||
) {
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.manuallyEnabled
|
||||
);
|
||||
} else if (
|
||||
lazy.Preferences.get("doh-rollout.doorhanger-decision", "") ==
|
||||
"UIDisabled"
|
||||
) {
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.optOut
|
||||
);
|
||||
} else {
|
||||
Services.telemetry.scalarSet(
|
||||
"networking.doh_heuristics_result",
|
||||
lazy.Heuristics.Telemetry.manuallyDisabled
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async disableHeuristics(state) {
|
||||
|
|
|
@ -105,6 +105,55 @@ export const Heuristics = {
|
|||
}
|
||||
return Ci.nsITRRSkipReason.TRR_FAILED;
|
||||
},
|
||||
|
||||
// Keep this in sync with the description of networking.doh_heuristics_result
|
||||
// defined in Scalars.yaml
|
||||
Telemetry: {
|
||||
incomplete: 0,
|
||||
pass: 1,
|
||||
optOut: 2,
|
||||
manuallyDisabled: 3,
|
||||
manuallyEnabled: 4,
|
||||
enterpriseDisabled: 5,
|
||||
enterprisePresent: 6,
|
||||
enterpriseEnabled: 7,
|
||||
vpn: 8,
|
||||
proxy: 9,
|
||||
nrpt: 10,
|
||||
browserParent: 11,
|
||||
modifiedRoots: 12,
|
||||
thirdPartyRoots: 13,
|
||||
google: 14,
|
||||
youtube: 15,
|
||||
zscalerCanary: 16,
|
||||
canary: 17,
|
||||
ignored: 18,
|
||||
|
||||
heuristicNames() {
|
||||
return [
|
||||
"google",
|
||||
"youtube",
|
||||
"zscalerCanary",
|
||||
"canary",
|
||||
"modifiedRoots",
|
||||
"browserParent",
|
||||
"thirdPartyRoots",
|
||||
"policy",
|
||||
"vpn",
|
||||
"proxy",
|
||||
"nrpt",
|
||||
];
|
||||
},
|
||||
|
||||
fromResults(results) {
|
||||
for (let label of Heuristics.Telemetry.heuristicNames()) {
|
||||
if (results[label] == Heuristics.DISABLE_DOH) {
|
||||
return Heuristics.Telemetry[label];
|
||||
}
|
||||
}
|
||||
return Heuristics.Telemetry.pass;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async function dnsLookup(hostname, resolveCanonicalName = false) {
|
||||
|
|
|
@ -28,6 +28,14 @@ add_task(async function testDoorhangerUserReject() {
|
|||
await ensureTRRMode(2);
|
||||
await checkHeuristicsTelemetry("enable_doh", "startup");
|
||||
|
||||
checkScalars([
|
||||
["networking.doh_heuristics_attempts", { value: 1 }],
|
||||
["networking.doh_heuristics_pass_count", { value: 1 }],
|
||||
["networking.doh_heuristics_result", { value: Heuristics.Telemetry.pass }],
|
||||
// All of the heuristics must be false.
|
||||
falseExpectations([]),
|
||||
]);
|
||||
|
||||
prefPromise = TestUtils.waitForPrefChange(
|
||||
prefs.DOORHANGER_USER_DECISION_PREF
|
||||
);
|
||||
|
@ -52,6 +60,17 @@ add_task(async function testDoorhangerUserReject() {
|
|||
ensureNoHeuristicsTelemetry();
|
||||
is(Preferences.get(prefs.BREADCRUMB_PREF), undefined, "Breadcrumb cleared.");
|
||||
|
||||
checkScalars([
|
||||
["networking.doh_heuristics_attempts", { value: 1 }],
|
||||
["networking.doh_heuristics_pass_count", { value: 1 }],
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.optOut },
|
||||
],
|
||||
// All of the heuristics must be false.
|
||||
falseExpectations([]),
|
||||
]);
|
||||
|
||||
// Simulate a network change.
|
||||
simulateNetworkChange();
|
||||
await ensureNoTRRModeChange(undefined);
|
||||
|
|
|
@ -12,7 +12,6 @@ add_task(setup);
|
|||
|
||||
add_task(async function testPlatformIndications() {
|
||||
// Check if the platform heuristics actually cause a "disable_doh" event
|
||||
|
||||
let { MockRegistrar } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/MockRegistrar.sys.mjs"
|
||||
);
|
||||
|
@ -45,6 +44,14 @@ add_task(async function testPlatformIndications() {
|
|||
is(Preferences.get(prefs.BREADCRUMB_PREF), true, "Breadcrumb saved.");
|
||||
await checkHeuristicsTelemetry("enable_doh", "startup");
|
||||
|
||||
checkScalars([
|
||||
["networking.doh_heuristics_attempts", { value: 1 }],
|
||||
["networking.doh_heuristics_pass_count", { value: 1 }],
|
||||
["networking.doh_heuristics_result", { value: Heuristics.Telemetry.pass }],
|
||||
// All of the heuristics must be false.
|
||||
falseExpectations([]),
|
||||
]);
|
||||
|
||||
await ensureTRRMode(2);
|
||||
|
||||
mockedLinkService.platformDNSIndications =
|
||||
|
@ -52,22 +59,68 @@ add_task(async function testPlatformIndications() {
|
|||
simulateNetworkChange();
|
||||
await ensureTRRMode(0);
|
||||
await checkHeuristicsTelemetry("disable_doh", "netchange");
|
||||
checkScalars(
|
||||
[
|
||||
["networking.doh_heuristics_attempts", { value: 2 }],
|
||||
["networking.doh_heuristics_pass_count", { value: 1 }],
|
||||
["networking.doh_heuristics_result", { value: Heuristics.Telemetry.vpn }],
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "vpn" }],
|
||||
].concat(falseExpectations(["vpn"]))
|
||||
);
|
||||
|
||||
mockedLinkService.platformDNSIndications =
|
||||
Ci.nsINetworkLinkService.PROXY_DETECTED;
|
||||
simulateNetworkChange();
|
||||
await ensureNoTRRModeChange(0);
|
||||
await checkHeuristicsTelemetry("disable_doh", "netchange");
|
||||
checkScalars(
|
||||
[
|
||||
["networking.doh_heuristics_attempts", { value: 3 }],
|
||||
["networking.doh_heuristics_pass_count", { value: 1 }],
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.proxy },
|
||||
],
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "vpn" }], // Was tripped earlier this session
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "proxy" }],
|
||||
].concat(falseExpectations(["vpn", "proxy"]))
|
||||
);
|
||||
|
||||
mockedLinkService.platformDNSIndications =
|
||||
Ci.nsINetworkLinkService.NRPT_DETECTED;
|
||||
simulateNetworkChange();
|
||||
await ensureNoTRRModeChange(0);
|
||||
await checkHeuristicsTelemetry("disable_doh", "netchange");
|
||||
checkScalars(
|
||||
[
|
||||
["networking.doh_heuristics_attempts", { value: 4 }],
|
||||
["networking.doh_heuristics_pass_count", { value: 1 }],
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.nrpt },
|
||||
],
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "vpn" }], // Was tripped earlier this session
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "proxy" }], // Was tripped earlier this session
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "nrpt" }],
|
||||
].concat(falseExpectations(["vpn", "proxy", "nrpt"]))
|
||||
);
|
||||
|
||||
mockedLinkService.platformDNSIndications =
|
||||
Ci.nsINetworkLinkService.NONE_DETECTED;
|
||||
simulateNetworkChange();
|
||||
await ensureTRRMode(2);
|
||||
await checkHeuristicsTelemetry("enable_doh", "netchange");
|
||||
checkScalars(
|
||||
[
|
||||
["networking.doh_heuristics_attempts", { value: 5 }],
|
||||
["networking.doh_heuristics_pass_count", { value: 2 }],
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.pass },
|
||||
],
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "vpn" }], // Was tripped earlier this session
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "proxy" }], // Was tripped earlier this session
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "nrpt" }], // Was tripped earlier this session
|
||||
].concat(falseExpectations(["vpn", "proxy", "nrpt"]))
|
||||
);
|
||||
});
|
||||
|
|
|
@ -47,6 +47,16 @@ add_task(async function testPolicyOverride() {
|
|||
await ensureNoTRRModeChange(undefined);
|
||||
ensureNoHeuristicsTelemetry();
|
||||
|
||||
checkScalars(
|
||||
[
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.enterprisePresent },
|
||||
],
|
||||
// All of the heuristics must be false.
|
||||
].concat(falseExpectations([]))
|
||||
);
|
||||
|
||||
// Simulate a network change.
|
||||
simulateNetworkChange();
|
||||
await ensureNoTRRModeChange(undefined);
|
||||
|
|
|
@ -83,6 +83,19 @@ add_task(async function testProviderSteering() {
|
|||
// Set enterprise roots enabled and ensure provider steering is disabled.
|
||||
Preferences.set("security.enterprise_roots.enabled", true);
|
||||
await testNetChangeResult(AUTO_TRR_URI, "disable_doh");
|
||||
checkScalars(
|
||||
[
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.modifiedRoots },
|
||||
],
|
||||
[
|
||||
"networking.doh_heuristic_ever_tripped",
|
||||
{ value: true, key: "modifiedRoots" },
|
||||
],
|
||||
// All of the other heuristics must be false.
|
||||
].concat(falseExpectations(["modifiedRoots"]))
|
||||
);
|
||||
Preferences.reset("security.enterprise_roots.enabled");
|
||||
|
||||
// Check that provider steering is enabled again after we reset above.
|
||||
|
@ -97,6 +110,20 @@ add_task(async function testProviderSteering() {
|
|||
await testNetChangeResult(AUTO_TRR_URI, "disable_doh");
|
||||
gDNSOverride.clearHostOverride(googleDomain);
|
||||
gDNSOverride.addIPOverride(googleDomain, googleIP);
|
||||
checkScalars(
|
||||
[
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.google },
|
||||
],
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "google" }],
|
||||
[
|
||||
"networking.doh_heuristic_ever_tripped",
|
||||
{ value: true, key: "modifiedRoots" },
|
||||
],
|
||||
// All of the other heuristics must be false.
|
||||
].concat(falseExpectations(["modifiedRoots", "google"]))
|
||||
);
|
||||
|
||||
// Check that provider steering is enabled again after we reset above.
|
||||
await testNetChangeResult(provider.uri, "enable_doh", provider.id);
|
||||
|
@ -104,4 +131,19 @@ add_task(async function testProviderSteering() {
|
|||
// Finally, provider steering should be disabled once we clear the override.
|
||||
gDNSOverride.clearHostOverride(TEST_DOMAIN);
|
||||
await testNetChangeResult(AUTO_TRR_URI, "enable_doh");
|
||||
|
||||
checkScalars(
|
||||
[
|
||||
[
|
||||
"networking.doh_heuristics_result",
|
||||
{ value: Heuristics.Telemetry.pass },
|
||||
],
|
||||
["networking.doh_heuristic_ever_tripped", { value: true, key: "google" }],
|
||||
[
|
||||
"networking.doh_heuristic_ever_tripped",
|
||||
{ value: true, key: "modifiedRoots" },
|
||||
],
|
||||
// All of the other heuristics must be false.
|
||||
].concat(falseExpectations(["modifiedRoots", "google"]))
|
||||
);
|
||||
});
|
||||
|
|
|
@ -4,9 +4,11 @@ ChromeUtils.defineESModuleGetters(this, {
|
|||
DoHConfigController: "resource:///modules/DoHConfig.sys.mjs",
|
||||
DoHController: "resource:///modules/DoHController.sys.mjs",
|
||||
DoHTestUtils: "resource://testing-common/DoHTestUtils.sys.mjs",
|
||||
Heuristics: "resource:///modules/DoHHeuristics.sys.mjs",
|
||||
Preferences: "resource://gre/modules/Preferences.sys.mjs",
|
||||
Region: "resource://gre/modules/Region.sys.mjs",
|
||||
RegionTestUtils: "resource://testing-common/RegionTestUtils.sys.mjs",
|
||||
TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
|
||||
});
|
||||
|
||||
|
@ -70,6 +72,7 @@ async function setup() {
|
|||
let oldCanRecord = Services.telemetry.canRecordExtended;
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
Services.telemetry.clearEvents();
|
||||
Services.telemetry.clearScalars();
|
||||
|
||||
// Enable the CFR.
|
||||
Preferences.set(CFR_PREF, JSON.stringify(CFR_JSON));
|
||||
|
@ -225,6 +228,49 @@ async function checkHeuristicsTelemetry(
|
|||
Services.telemetry.clearEvents();
|
||||
}
|
||||
|
||||
// Generates an array of expectations for the ever_tripped scalar
|
||||
// containing false and key, except for the keyes contained in
|
||||
// the `except` parameter.
|
||||
function falseExpectations(except) {
|
||||
return Heuristics.Telemetry.heuristicNames()
|
||||
.map(e => [
|
||||
"networking.doh_heuristic_ever_tripped",
|
||||
{ value: false, key: e },
|
||||
])
|
||||
.filter(e => except && !except.includes(e[1].key));
|
||||
}
|
||||
|
||||
function checkScalars(expectations) {
|
||||
// expectations: [[scalarname: expectationObject]]
|
||||
// expectationObject: {value, key}
|
||||
let snapshot = TelemetryTestUtils.getProcessScalars("parent", false, false);
|
||||
let keyedSnapshot = TelemetryTestUtils.getProcessScalars(
|
||||
"parent",
|
||||
true,
|
||||
false
|
||||
);
|
||||
for (let ex of expectations) {
|
||||
let scalarName = ex[0];
|
||||
let exObject = ex[1];
|
||||
if (exObject.key) {
|
||||
TelemetryTestUtils.assertKeyedScalar(
|
||||
keyedSnapshot,
|
||||
scalarName,
|
||||
exObject.key,
|
||||
exObject.value,
|
||||
`${scalarName} expected to have ${exObject.value}, key: ${exObject.key}`
|
||||
);
|
||||
} else {
|
||||
TelemetryTestUtils.assertScalar(
|
||||
snapshot,
|
||||
scalarName,
|
||||
exObject.value,
|
||||
`${scalarName} expected to have ${exObject.value}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function checkHeuristicsTelemetryMultiple(expectedEvaluateReasons) {
|
||||
let events;
|
||||
await TestUtils.waitForCondition(() => {
|
||||
|
|
|
@ -61,11 +61,11 @@ class CardContainer extends MozLitElement {
|
|||
if (this.preserveCollapseState) {
|
||||
Services.prefs.setBoolPref(this.openStatePref, this.isExpanded);
|
||||
}
|
||||
if (!this.isExpanded && this.shortPageName) {
|
||||
// Record telemetry
|
||||
// Record telemetry
|
||||
if (this.shortPageName) {
|
||||
Services.telemetry.recordEvent(
|
||||
"firefoxview_next",
|
||||
"card_collapsed",
|
||||
this.isExpanded ? "card_expanded" : "card_collapsed",
|
||||
"card_container",
|
||||
null,
|
||||
{
|
||||
|
@ -75,6 +75,15 @@ class CardContainer extends MozLitElement {
|
|||
}
|
||||
}
|
||||
|
||||
viewAllClicked() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("card-container-view-all", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
|
@ -111,6 +120,7 @@ class CardContainer extends MozLitElement {
|
|||
</summary>
|
||||
<a
|
||||
href="about:firefoxview-next#${this.shortPageName}"
|
||||
@click=${this.viewAllClicked}
|
||||
class="view-all-link"
|
||||
data-l10n-id="firefoxview-view-all-link"
|
||||
?hidden=${!this.showViewAll}
|
||||
|
|
|
@ -180,7 +180,7 @@ export const SyncedTabsErrorHandler = {
|
|||
},
|
||||
[ErrorType.SIGNED_OUT]: {
|
||||
header: "firefoxview-tabpickup-signed-out-header",
|
||||
description: "firefoxview-tabpickup-signed-out-description",
|
||||
description: "firefoxview-tabpickup-signed-out-description2",
|
||||
buttonLabel: "firefoxview-tabpickup-signed-out-primarybutton",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -22,7 +22,15 @@ function changePage(page) {
|
|||
);
|
||||
(currentCategoryButton || navigation.categoryButtons[0]).focus();
|
||||
}
|
||||
}
|
||||
|
||||
function recordTelemetry(source, eventTarget) {
|
||||
let page = "recentbrowsing";
|
||||
if (source === "category-navigation") {
|
||||
page = eventTarget.parentNode.currentCategory;
|
||||
} else if (source === "view-all") {
|
||||
page = eventTarget.shortPageName;
|
||||
}
|
||||
// Record telemetry
|
||||
Services.telemetry.recordEvent(
|
||||
"firefoxview_next",
|
||||
|
@ -30,7 +38,8 @@ function changePage(page) {
|
|||
"navigation",
|
||||
null,
|
||||
{
|
||||
page: navigation.currentCategory,
|
||||
page,
|
||||
source,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -44,6 +53,10 @@ window.addEventListener("DOMContentLoaded", async () => {
|
|||
window.addEventListener("hashchange", onHashChange);
|
||||
window.addEventListener("change-category", function (event) {
|
||||
location.hash = event.target.getAttribute("name");
|
||||
recordTelemetry("category-navigation", event.target);
|
||||
});
|
||||
window.addEventListener("card-container-view-all", function (event) {
|
||||
recordTelemetry("view-all", event.originalTarget);
|
||||
});
|
||||
if (document.location.hash) {
|
||||
changePage(document.location.hash.substring(1));
|
||||
|
|
|
@ -105,6 +105,7 @@ class HistoryInView extends ViewPage {
|
|||
migrationWizardDialog: "#migrationWizardDialog",
|
||||
emptyState: "fxview-empty-state",
|
||||
lists: { all: "fxview-tab-list" },
|
||||
panelList: "panel-list",
|
||||
};
|
||||
|
||||
static properties = {
|
||||
|
@ -210,6 +211,7 @@ class HistoryInView extends ViewPage {
|
|||
|
||||
deleteFromHistory(e) {
|
||||
lazy.PlacesUtils.history.remove(this.triggerNode.url);
|
||||
this.recordContextMenuTelemetry("delete-from-history", e);
|
||||
}
|
||||
|
||||
async onChangeSortOption(e) {
|
||||
|
@ -268,7 +270,7 @@ class HistoryInView extends ViewPage {
|
|||
|
||||
panelListTemplate() {
|
||||
return html`
|
||||
<panel-list slot="menu">
|
||||
<panel-list slot="menu" data-tab-type="history">
|
||||
<panel-item
|
||||
@click=${this.deleteFromHistory}
|
||||
data-l10n-id="firefoxview-history-context-delete"
|
||||
|
|
|
@ -22,4 +22,4 @@ BROWSER_CHROME_MANIFESTS += [
|
|||
"tests/browser/firefoxview-next/browser.ini",
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.ini"]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.toml"]
|
||||
|
|
|
@ -257,18 +257,21 @@ class OpenTabsInViewCard extends ViewPage {
|
|||
}
|
||||
|
||||
static queries = {
|
||||
cardEl: "card-container",
|
||||
panelList: "panel-list",
|
||||
tabList: "fxview-tab-list",
|
||||
};
|
||||
|
||||
closeTab() {
|
||||
closeTab(e) {
|
||||
const tab = this.triggerNode.tabElement;
|
||||
const browserWindow = tab.ownerGlobal;
|
||||
browserWindow.gBrowser.removeTab(tab, { animate: true });
|
||||
this.recordContextMenuTelemetry("close-tab", e);
|
||||
}
|
||||
|
||||
panelListTemplate() {
|
||||
return html`
|
||||
<panel-list slot="menu">
|
||||
<panel-list slot="menu" data-tab-type="opentabs">
|
||||
<panel-item
|
||||
data-l10n-id="fxviewtabrow-close-tab"
|
||||
data-l10n-attrs="accesskey"
|
||||
|
|
|
@ -269,7 +269,7 @@ class SyncedTabsInView extends ViewPage {
|
|||
|
||||
panelListTemplate() {
|
||||
return html`
|
||||
<panel-list slot="menu">
|
||||
<panel-list slot="menu" data-tab-type="syncedtabs">
|
||||
<panel-item
|
||||
@click=${this.openInNewWindow}
|
||||
data-l10n-id="fxviewtabrow-open-in-window"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
support-files = ../head.js
|
||||
|
||||
[browser_firefoxview_next.js]
|
||||
[browser_firefoxview_next_general_telemetry.js]
|
||||
[browser_history_firefoxview_next.js]
|
||||
[browser_recentlyclosed_firefoxview_next.js]
|
||||
[browser_syncedtabs_errors_firefoxview_next.js]
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from ../head.js */
|
||||
|
||||
const FXVIEW_NEXT_ENABLED_PREF = "browser.tabs.firefox-view-next";
|
||||
const CARD_COLLAPSED_EVENT = [
|
||||
["firefoxview_next", "card_collapsed", "card_container", undefined],
|
||||
];
|
||||
const CARD_EXPANDED_EVENT = [
|
||||
["firefoxview_next", "card_expanded", "card_container", undefined],
|
||||
];
|
||||
|
||||
async function cardCollapsedTelemetry() {
|
||||
await TestUtils.waitForCondition(
|
||||
() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
false
|
||||
).parent;
|
||||
return events && events.length >= 1;
|
||||
},
|
||||
"Waiting for card_collapsed firefoxview_next telemetry event.",
|
||||
200,
|
||||
100
|
||||
);
|
||||
|
||||
TelemetryTestUtils.assertEvents(
|
||||
CARD_COLLAPSED_EVENT,
|
||||
{ category: "firefoxview_next" },
|
||||
{ clear: true, process: "parent" }
|
||||
);
|
||||
}
|
||||
|
||||
async function cardExpandedTelemetry() {
|
||||
await TestUtils.waitForCondition(
|
||||
() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
false
|
||||
).parent;
|
||||
return events && events.length >= 1;
|
||||
},
|
||||
"Waiting for card_expanded firefoxview_next telemetry event.",
|
||||
200,
|
||||
100
|
||||
);
|
||||
|
||||
TelemetryTestUtils.assertEvents(
|
||||
CARD_EXPANDED_EVENT,
|
||||
{ category: "firefoxview_next" },
|
||||
{ clear: true, process: "parent" }
|
||||
);
|
||||
}
|
||||
|
||||
async function navigationTelemetry(changePageEvent) {
|
||||
await TestUtils.waitForCondition(
|
||||
() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
false
|
||||
).parent;
|
||||
return events && events.length >= 1;
|
||||
},
|
||||
"Waiting for change_page firefoxview_next telemetry event.",
|
||||
200,
|
||||
100
|
||||
);
|
||||
|
||||
TelemetryTestUtils.assertEvents(
|
||||
changePageEvent,
|
||||
{ category: "firefoxview_next" },
|
||||
{ clear: true, process: "parent" }
|
||||
);
|
||||
}
|
||||
|
||||
async function contextMenuTelemetry(contextMenuEvent) {
|
||||
await TestUtils.waitForCondition(
|
||||
() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
false
|
||||
).parent;
|
||||
return events && events.length >= 1;
|
||||
},
|
||||
"Waiting for context_menu firefoxview_next telemetry event.",
|
||||
200,
|
||||
100
|
||||
);
|
||||
|
||||
TelemetryTestUtils.assertEvents(
|
||||
contextMenuEvent,
|
||||
{ category: "firefoxview_next" },
|
||||
{ clear: true, process: "parent" }
|
||||
);
|
||||
}
|
||||
|
||||
add_setup(async () => {
|
||||
await SpecialPowers.pushPrefEnv({ set: [[FXVIEW_NEXT_ENABLED_PREF, true]] });
|
||||
registerCleanupFunction(async () => {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
clearHistory();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_collapse_and_expand_card() {
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
// Test using Recently Closed card on Recent Browsing page
|
||||
let recentlyClosedComponent = document.querySelector(
|
||||
"view-recentlyclosed[slot=recentlyclosed]"
|
||||
);
|
||||
let cardContainer = recentlyClosedComponent.cardEl;
|
||||
is(
|
||||
cardContainer.isExpanded,
|
||||
true,
|
||||
"The card-container is expanded initially"
|
||||
);
|
||||
await clearAllParentTelemetryEvents();
|
||||
// Click the summary to collapse the details disclosure
|
||||
await EventUtils.synthesizeMouseAtCenter(
|
||||
cardContainer.summaryEl,
|
||||
{},
|
||||
content
|
||||
);
|
||||
is(
|
||||
cardContainer.detailsEl.hasAttribute("open"),
|
||||
false,
|
||||
"The card-container is collapsed"
|
||||
);
|
||||
await cardCollapsedTelemetry();
|
||||
// Click the summary again to expand the details disclosure
|
||||
await EventUtils.synthesizeMouseAtCenter(
|
||||
cardContainer.summaryEl,
|
||||
{},
|
||||
content
|
||||
);
|
||||
is(
|
||||
cardContainer.detailsEl.hasAttribute("open"),
|
||||
true,
|
||||
"The card-container is expanded"
|
||||
);
|
||||
await cardExpandedTelemetry();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_change_page_telemetry() {
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
let changePageEvent = [
|
||||
[
|
||||
"firefoxview_next",
|
||||
"change_page",
|
||||
"navigation",
|
||||
undefined,
|
||||
{ page: "recentlyclosed", source: "category-navigation" },
|
||||
],
|
||||
];
|
||||
await clearAllParentTelemetryEvents();
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigationTelemetry(changePageEvent);
|
||||
navigateToCategory(document, "recentbrowsing");
|
||||
|
||||
let openTabsComponent = document.querySelector(
|
||||
"view-opentabs[slot=opentabs]"
|
||||
);
|
||||
let cardContainer =
|
||||
openTabsComponent.shadowRoot.querySelector("view-opentabs-card").cardEl;
|
||||
let viewAllLink = cardContainer.viewAllLink;
|
||||
changePageEvent = [
|
||||
[
|
||||
"firefoxview_next",
|
||||
"change_page",
|
||||
"navigation",
|
||||
undefined,
|
||||
{ page: "opentabs", source: "view-all" },
|
||||
],
|
||||
];
|
||||
await clearAllParentTelemetryEvents();
|
||||
await EventUtils.synthesizeMouseAtCenter(viewAllLink, {}, content);
|
||||
await navigationTelemetry(changePageEvent);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_context_menu_telemetry() {
|
||||
await PlacesUtils.history.insert({
|
||||
url: URLs[0],
|
||||
title: "Example Domain 1",
|
||||
visits: [{ date: new Date() }],
|
||||
});
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
// Test open tabs telemetry
|
||||
let openTabsComponent = document.querySelector(
|
||||
"view-opentabs[slot=opentabs]"
|
||||
);
|
||||
let tabList =
|
||||
openTabsComponent.shadowRoot.querySelector("view-opentabs-card").tabList;
|
||||
let firstItem = tabList.rowEls[0];
|
||||
let panelList =
|
||||
openTabsComponent.shadowRoot.querySelector(
|
||||
"view-opentabs-card"
|
||||
).panelList;
|
||||
await EventUtils.synthesizeMouseAtCenter(firstItem.buttonEl, {}, content);
|
||||
await BrowserTestUtils.waitForEvent(panelList, "shown");
|
||||
await clearAllParentTelemetryEvents();
|
||||
let copyLinkOption = panelList.children[1];
|
||||
let contextMenuEvent = [
|
||||
[
|
||||
"firefoxview_next",
|
||||
"context_menu",
|
||||
"tabs",
|
||||
undefined,
|
||||
{ menu_action: "copy-link", data_type: "opentabs" },
|
||||
],
|
||||
];
|
||||
await EventUtils.synthesizeMouseAtCenter(copyLinkOption, {}, content);
|
||||
await contextMenuTelemetry(contextMenuEvent);
|
||||
|
||||
// Open new tab to test 'Close tab' menu option
|
||||
window.openTrustedLinkIn("about:robots", "tab");
|
||||
await switchToFxViewTab(browser.ownerGlobal);
|
||||
firstItem = tabList.rowEls[0];
|
||||
await EventUtils.synthesizeMouseAtCenter(firstItem.buttonEl, {}, content);
|
||||
await BrowserTestUtils.waitForEvent(panelList, "shown");
|
||||
await clearAllParentTelemetryEvents();
|
||||
let closeTabOption = panelList.children[0];
|
||||
contextMenuEvent = [
|
||||
[
|
||||
"firefoxview_next",
|
||||
"context_menu",
|
||||
"tabs",
|
||||
undefined,
|
||||
{ menu_action: "close-tab", data_type: "opentabs" },
|
||||
],
|
||||
];
|
||||
await EventUtils.synthesizeMouseAtCenter(closeTabOption, {}, content);
|
||||
await contextMenuTelemetry(contextMenuEvent);
|
||||
|
||||
// Test history context menu options
|
||||
navigateToCategory(document, "history");
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
await TestUtils.waitForCondition(() => historyComponent.fullyUpdated);
|
||||
let firstTabList = historyComponent.lists[0];
|
||||
firstItem = firstTabList.rowEls[0];
|
||||
panelList = historyComponent.panelList;
|
||||
await EventUtils.synthesizeMouseAtCenter(firstItem.buttonEl, {}, content);
|
||||
await BrowserTestUtils.waitForEvent(panelList, "shown");
|
||||
await clearAllParentTelemetryEvents();
|
||||
let panelItems = Array.from(panelList.children).filter(
|
||||
panelItem => panelItem.nodeName === "PANEL-ITEM"
|
||||
);
|
||||
let openInNewWindowOption = panelItems[1];
|
||||
contextMenuEvent = [
|
||||
[
|
||||
"firefoxview_next",
|
||||
"context_menu",
|
||||
"tabs",
|
||||
undefined,
|
||||
{ menu_action: "open-in-new-window", data_type: "history" },
|
||||
],
|
||||
];
|
||||
let newWindowPromise = BrowserTestUtils.waitForNewWindow({
|
||||
url: URLs[0],
|
||||
});
|
||||
await EventUtils.synthesizeMouseAtCenter(
|
||||
openInNewWindowOption,
|
||||
{},
|
||||
content
|
||||
);
|
||||
let win = await newWindowPromise;
|
||||
await contextMenuTelemetry(contextMenuEvent);
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
|
||||
await EventUtils.synthesizeMouseAtCenter(firstItem.buttonEl, {}, content);
|
||||
await BrowserTestUtils.waitForEvent(panelList, "shown");
|
||||
await clearAllParentTelemetryEvents();
|
||||
let openInPrivateWindowOption = panelItems[2];
|
||||
contextMenuEvent = [
|
||||
[
|
||||
"firefoxview_next",
|
||||
"context_menu",
|
||||
"tabs",
|
||||
undefined,
|
||||
{ menu_action: "open-in-private-window", data_type: "history" },
|
||||
],
|
||||
];
|
||||
newWindowPromise = BrowserTestUtils.waitForNewWindow({
|
||||
url: URLs[0],
|
||||
});
|
||||
await EventUtils.synthesizeMouseAtCenter(
|
||||
openInPrivateWindowOption,
|
||||
{},
|
||||
content
|
||||
);
|
||||
win = await newWindowPromise;
|
||||
await contextMenuTelemetry(contextMenuEvent);
|
||||
ok(
|
||||
PrivateBrowsingUtils.isWindowPrivate(win),
|
||||
"Should have opened a private window."
|
||||
);
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
|
||||
await EventUtils.synthesizeMouseAtCenter(firstItem.buttonEl, {}, content);
|
||||
await BrowserTestUtils.waitForEvent(panelList, "shown");
|
||||
await clearAllParentTelemetryEvents();
|
||||
let deleteFromHistoryOption = panelItems[0];
|
||||
contextMenuEvent = [
|
||||
[
|
||||
"firefoxview_next",
|
||||
"context_menu",
|
||||
"tabs",
|
||||
undefined,
|
||||
{ menu_action: "delete-from-history", data_type: "history" },
|
||||
],
|
||||
];
|
||||
await EventUtils.synthesizeMouseAtCenter(
|
||||
deleteFromHistoryOption,
|
||||
{},
|
||||
content
|
||||
);
|
||||
await contextMenuTelemetry(contextMenuEvent);
|
||||
|
||||
// clean up extra tabs
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
|
||||
}
|
||||
});
|
||||
});
|
|
@ -47,17 +47,6 @@ async function openFirefoxView(win) {
|
|||
);
|
||||
}
|
||||
|
||||
function navigateToHistory(document) {
|
||||
// Navigate to Recently closed tabs page/view
|
||||
const navigation = document.querySelector("fxview-category-navigation");
|
||||
let historyNavButton = Array.from(navigation.categoryButtons).find(
|
||||
categoryButton => {
|
||||
return categoryButton.name === "history";
|
||||
}
|
||||
);
|
||||
historyNavButton.buttonEl.click();
|
||||
}
|
||||
|
||||
async function addHistoryItems(dateAdded) {
|
||||
await PlacesUtils.history.insert({
|
||||
url: URLs[0],
|
||||
|
@ -101,7 +90,7 @@ add_task(async function test_list_ordering() {
|
|||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
navigateToHistory(document);
|
||||
navigateToCategory(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
@ -158,7 +147,7 @@ add_task(async function test_empty_states() {
|
|||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
navigateToHistory(document);
|
||||
navigateToCategory(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
@ -246,7 +235,7 @@ add_task(async function test_observers_removed_when_view_is_hidden() {
|
|||
);
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToHistory(document);
|
||||
navigateToCategory(document, "history");
|
||||
const historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
const visitList = await TestUtils.waitForCondition(() =>
|
||||
|
|
|
@ -15,12 +15,6 @@ const RECENTLY_CLOSED_EVENT = [
|
|||
const DISMISS_CLOSED_TAB_EVENT = [
|
||||
["firefoxview_next", "dismiss_closed_tab", "tabs", undefined],
|
||||
];
|
||||
const CHANGE_PAGE_EVENT = [
|
||||
["firefoxview_next", "change_page", "navigation", undefined],
|
||||
];
|
||||
const CARD_COLLAPSED_EVENT = [
|
||||
["firefoxview_next", "card_collapsed", "card_container", undefined],
|
||||
];
|
||||
const initialTab = gBrowser.selectedTab;
|
||||
|
||||
function isElInViewport(element) {
|
||||
|
@ -248,48 +242,6 @@ async function recentlyClosedDismissTelemetry() {
|
|||
);
|
||||
}
|
||||
|
||||
async function navigationTelemetry() {
|
||||
await TestUtils.waitForCondition(
|
||||
() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
false
|
||||
).parent;
|
||||
return events && events.length >= 1;
|
||||
},
|
||||
"Waiting for change_page firefoxview_next telemetry event.",
|
||||
200,
|
||||
100
|
||||
);
|
||||
|
||||
TelemetryTestUtils.assertEvents(
|
||||
CHANGE_PAGE_EVENT,
|
||||
{ category: "firefoxview_next" },
|
||||
{ clear: true, process: "parent" }
|
||||
);
|
||||
}
|
||||
|
||||
async function cardCollapsedTelemetry() {
|
||||
await TestUtils.waitForCondition(
|
||||
() => {
|
||||
let events = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
||||
false
|
||||
).parent;
|
||||
return events && events.length >= 1;
|
||||
},
|
||||
"Waiting for card_collapsed firefoxview_next telemetry event.",
|
||||
200,
|
||||
100
|
||||
);
|
||||
|
||||
TelemetryTestUtils.assertEvents(
|
||||
CARD_COLLAPSED_EVENT,
|
||||
{ category: "firefoxview_next" },
|
||||
{ clear: true, process: "parent" }
|
||||
);
|
||||
}
|
||||
|
||||
add_setup(async () => {
|
||||
await SpecialPowers.pushPrefEnv({ set: [[FXVIEW_NEXT_ENABLED_PREF, true]] });
|
||||
registerCleanupFunction(async () => {
|
||||
|
@ -309,7 +261,6 @@ add_task(async function test_list_ordering() {
|
|||
is(document.location.href, "about:firefoxview-next");
|
||||
await clearAllParentTelemetryEvents();
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigationTelemetry();
|
||||
let [cardMainSlotNode, listItems] = await waitForRecentlyClosedTabsList(
|
||||
document
|
||||
);
|
||||
|
@ -329,49 +280,6 @@ add_task(async function test_list_ordering() {
|
|||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(async function test_collapse_card() {
|
||||
const { cleanup } = await prepareSingleClosedTab();
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
let recentlyClosedComponent = document.querySelector(
|
||||
"view-recentlyclosed[slot=recentlyclosed]"
|
||||
);
|
||||
let cardContainer = recentlyClosedComponent.cardEl;
|
||||
is(
|
||||
cardContainer.isExpanded,
|
||||
true,
|
||||
"The card-container is expanded initially"
|
||||
);
|
||||
await clearAllParentTelemetryEvents();
|
||||
// Click the summary to collapse the details disclosure
|
||||
await EventUtils.synthesizeMouseAtCenter(
|
||||
cardContainer.summaryEl,
|
||||
{},
|
||||
content
|
||||
);
|
||||
is(
|
||||
cardContainer.detailsEl.hasAttribute("open"),
|
||||
false,
|
||||
"The card-container is collapsed"
|
||||
);
|
||||
await cardCollapsedTelemetry();
|
||||
// Click the summary again to expand the details disclosure
|
||||
await EventUtils.synthesizeMouseAtCenter(
|
||||
cardContainer.summaryEl,
|
||||
{},
|
||||
content
|
||||
);
|
||||
is(
|
||||
cardContainer.detailsEl.hasAttribute("open"),
|
||||
true,
|
||||
"The card-container is expanded"
|
||||
);
|
||||
});
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
/**
|
||||
* Asserts that an out-of-band update to recently-closed tabs results in the correct update to the tab list
|
||||
*/
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[DEFAULT]
|
||||
|
||||
[test_card_container.html]
|
||||
[test_fxview_category_navigation.html]
|
||||
[test_fxview_tab_list.html]
|
||||
[test_opentabs.html]
|
9
browser/components/firefoxview/tests/chrome/chrome.toml
Normal file
9
browser/components/firefoxview/tests/chrome/chrome.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[DEFAULT]
|
||||
|
||||
["test_card_container.html"]
|
||||
|
||||
["test_fxview_category_navigation.html"]
|
||||
|
||||
["test_fxview_tab_list.html"]
|
||||
|
||||
["test_opentabs.html"]
|
|
@ -35,7 +35,7 @@
|
|||
}
|
||||
|
||||
function getRowsForCard(card) {
|
||||
return card.shadowRoot.querySelector("fxview-tab-list").rowEls;
|
||||
return card.tabList.rowEls;
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
|
|
|
@ -121,17 +121,33 @@ export class ViewPage extends MozLitElement {
|
|||
null,
|
||||
Ci.nsIClipboard.kGlobalClipboard
|
||||
);
|
||||
this.recordContextMenuTelemetry("copy-link", e);
|
||||
}
|
||||
|
||||
openInNewWindow(e) {
|
||||
this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
|
||||
private: false,
|
||||
});
|
||||
this.recordContextMenuTelemetry("open-in-new-window", e);
|
||||
}
|
||||
|
||||
openInNewPrivateWindow(e) {
|
||||
this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
|
||||
private: true,
|
||||
});
|
||||
this.recordContextMenuTelemetry("open-in-private-window", e);
|
||||
}
|
||||
|
||||
recordContextMenuTelemetry(menuAction, event) {
|
||||
Services.telemetry.recordEvent(
|
||||
"firefoxview_next",
|
||||
"context_menu",
|
||||
"tabs",
|
||||
null,
|
||||
{
|
||||
menu_action: menuAction,
|
||||
data_type: event.target.panel.dataset.tabType,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@ class MigrationUtils {
|
|||
"MigrationWizard:BeginMigration": { wantUntrusted: true },
|
||||
"MigrationWizard:RequestSafariPermissions": { wantUntrusted: true },
|
||||
"MigrationWizard:SelectSafariPasswordFile": { wantUntrusted: true },
|
||||
"MigrationWizard:OpenAboutAddons": { wantUntrusted: true },
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -148,6 +148,11 @@ export class MigrationWizardChild extends JSWindowActorChild {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "MigrationWizard:OpenAboutAddons": {
|
||||
this.sendAsyncMessage("OpenAboutAddons");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,6 +161,12 @@ export class MigrationWizardParent extends JSWindowActorParent {
|
|||
this.#recordEvent(message.data.type, message.data.args);
|
||||
break;
|
||||
}
|
||||
|
||||
case "OpenAboutAddons": {
|
||||
let browser = this.browsingContext.top.embedderElement;
|
||||
this.#openAboutAddons(browser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -769,4 +775,16 @@ export class MigrationWizardParent extends JSWindowActorParent {
|
|||
resourceTypes: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the about:addons page in a new background tab in the same window
|
||||
* as the passed browser.
|
||||
*
|
||||
* @param {Element} browser
|
||||
* The browser element requesting that about:addons opens.
|
||||
*/
|
||||
#openAboutAddons(browser) {
|
||||
let window = browser.ownerGlobal;
|
||||
window.openTrustedLinkIn("about:addons", "tab", { inBackground: true });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ export class MigrationWizard extends HTMLElement {
|
|||
#selectAllCheckbox = null;
|
||||
#resourceSummary = null;
|
||||
#expandedDetails = false;
|
||||
#extensionsSuccessLink = null;
|
||||
|
||||
static get markup() {
|
||||
return `
|
||||
|
@ -133,7 +134,7 @@ export class MigrationWizard extends HTMLElement {
|
|||
<div data-resource-type="EXTENSIONS" class="resource-progress-group">
|
||||
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
|
||||
<span data-l10n-id="migration-extensions-option-label"></span>
|
||||
<a class="message-text deemphasized-text"></a>
|
||||
<a id="extensions-success-link" href="about:addons" class="message-text deemphasized-text"></a>
|
||||
<span class="message-text deemphasized-text"></span>
|
||||
<a class="support-text deemphasized-text"></a>
|
||||
</div>
|
||||
|
@ -333,6 +334,11 @@ export class MigrationWizard extends HTMLElement {
|
|||
);
|
||||
this.#safariPasswordImportSelectButton.addEventListener("click", this);
|
||||
|
||||
this.#extensionsSuccessLink = shadow.querySelector(
|
||||
"#extensions-success-link"
|
||||
);
|
||||
this.#extensionsSuccessLink.addEventListener("click", this);
|
||||
|
||||
this.#shadowRoot = shadow;
|
||||
}
|
||||
|
||||
|
@ -649,6 +655,8 @@ export class MigrationWizard extends HTMLElement {
|
|||
let resourceGroups = progressPage.querySelectorAll(
|
||||
".resource-progress-group"
|
||||
);
|
||||
this.#extensionsSuccessLink.textContent = "";
|
||||
|
||||
let totalProgressGroups = Object.keys(state.progress).length;
|
||||
let remainingProgressGroups = totalProgressGroups;
|
||||
let totalWarnings = 0;
|
||||
|
@ -663,7 +671,6 @@ export class MigrationWizard extends HTMLElement {
|
|||
|
||||
let progressIcon = group.querySelector(".progress-icon");
|
||||
let messageText = group.querySelector("span.message-text");
|
||||
let extensionsSuccessLink = group.querySelector("a.message-text");
|
||||
let supportLink = group.querySelector(".support-text");
|
||||
|
||||
let labelSpan = group.querySelector("span[default-data-l10n-id]");
|
||||
|
@ -681,10 +688,7 @@ export class MigrationWizard extends HTMLElement {
|
|||
}
|
||||
}
|
||||
messageText.textContent = "";
|
||||
if (extensionsSuccessLink) {
|
||||
extensionsSuccessLink.textContent = "";
|
||||
extensionsSuccessLink.removeAttribute("href");
|
||||
}
|
||||
|
||||
if (supportLink) {
|
||||
supportLink.textContent = "";
|
||||
supportLink.removeAttribute("href");
|
||||
|
@ -698,10 +702,6 @@ export class MigrationWizard extends HTMLElement {
|
|||
);
|
||||
progressIcon.setAttribute("state", "loading");
|
||||
messageText.textContent = "";
|
||||
if (extensionsSuccessLink) {
|
||||
extensionsSuccessLink.textContent = "";
|
||||
extensionsSuccessLink.removeAttribute("href");
|
||||
}
|
||||
supportLink.textContent = "";
|
||||
supportLink.removeAttribute("href");
|
||||
// With no status text, we re-insert the so that the status
|
||||
|
@ -721,8 +721,7 @@ export class MigrationWizard extends HTMLElement {
|
|||
MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.EXTENSIONS
|
||||
) {
|
||||
messageText.textContent = "";
|
||||
extensionsSuccessLink.href = "about:addons";
|
||||
extensionsSuccessLink.textContent =
|
||||
this.#extensionsSuccessLink.textContent =
|
||||
state.progress[resourceType].message;
|
||||
}
|
||||
remainingProgressGroups--;
|
||||
|
@ -755,8 +754,7 @@ export class MigrationWizard extends HTMLElement {
|
|||
MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.EXTENSIONS
|
||||
) {
|
||||
messageText.textContent = "";
|
||||
extensionsSuccessLink.href = "about:addons";
|
||||
extensionsSuccessLink.textContent =
|
||||
this.#extensionsSuccessLink.textContent =
|
||||
state.progress[resourceType].message;
|
||||
}
|
||||
remainingProgressGroups--;
|
||||
|
@ -1263,6 +1261,13 @@ export class MigrationWizard extends HTMLElement {
|
|||
}
|
||||
} else if (event.target == this.#safariPasswordImportSelectButton) {
|
||||
this.#selectSafariPasswordFile();
|
||||
} else if (event.target == this.#extensionsSuccessLink) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("MigrationWizard:OpenAboutAddons", {
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.ini"]
|
|||
|
||||
MARIONETTE_UNIT_MANIFESTS += ["tests/marionette/manifest.ini"]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.ini"]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.toml"]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.ini"]
|
||||
|
||||
|
|
|
@ -29,8 +29,9 @@ let gFluentStrings = new Localization([
|
|||
* @param {string} description.linkText
|
||||
* The text content for the <a> element that should be displayed to the user
|
||||
* for the particular state.
|
||||
* @returns {Promise<undefined>}
|
||||
*/
|
||||
function assertExtensionsProgressState(wizard, state, description) {
|
||||
async function assertExtensionsProgressState(wizard, state, description) {
|
||||
let shadow = wizard.openOrClosedShadowRoot;
|
||||
|
||||
// Make sure that we're showing the progress page first.
|
||||
|
@ -47,31 +48,52 @@ function assertExtensionsProgressState(wizard, state, description) {
|
|||
let progressIcon = progressGroup.querySelector(".progress-icon");
|
||||
let messageText = progressGroup.querySelector("span.message-text");
|
||||
let supportLink = progressGroup.querySelector(".support-text");
|
||||
let extensionsSuccessLink = progressGroup.querySelector("a.message-text");
|
||||
let extensionsSuccessLink = progressGroup.querySelector(
|
||||
"#extensions-success-link"
|
||||
);
|
||||
|
||||
if (state == MigrationWizardConstants.PROGRESS_VALUE.SUCCESS) {
|
||||
Assert.stringMatches(progressIcon.getAttribute("state"), "success");
|
||||
Assert.stringMatches(messageText.textContent, "");
|
||||
|
||||
Assert.stringMatches(extensionsSuccessLink.href, "about:addons");
|
||||
Assert.stringMatches(
|
||||
extensionsSuccessLink.textContent,
|
||||
description.message
|
||||
);
|
||||
Assert.stringMatches(supportLink.textContent, "");
|
||||
await assertSuccessLink(extensionsSuccessLink, description.message);
|
||||
} else if (state == MigrationWizardConstants.PROGRESS_VALUE.WARNING) {
|
||||
Assert.stringMatches(progressIcon.getAttribute("state"), "warning");
|
||||
Assert.stringMatches(messageText.textContent, description.message);
|
||||
Assert.stringMatches(supportLink.textContent, description.linkText);
|
||||
Assert.stringMatches(supportLink.href, description.linkURL);
|
||||
await assertSuccessLink(extensionsSuccessLink, "");
|
||||
} else if (state == MigrationWizardConstants.PROGRESS_VALUE.INFO) {
|
||||
Assert.stringMatches(progressIcon.getAttribute("state"), "info");
|
||||
Assert.stringMatches(extensionsSuccessLink.href, description.linkURL);
|
||||
Assert.stringMatches(
|
||||
extensionsSuccessLink.textContent,
|
||||
description.message
|
||||
);
|
||||
Assert.stringMatches(supportLink.textContent, "");
|
||||
await assertSuccessLink(extensionsSuccessLink, description.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the extensions migration success link has the right
|
||||
* text content, and if the text content is non-blank, ensures that
|
||||
* clicking on the link opens up about:addons in a background tab.
|
||||
*
|
||||
* The about:addons tab will be automatically closed before proceeding.
|
||||
*
|
||||
* @param {Element} link
|
||||
* The extensions migration success link element.
|
||||
* @param {string} message
|
||||
* The expected string to appear in the link textContent. If the
|
||||
* link is not expected to appear, this should be the empty string.
|
||||
* @returns {Promise<undefined>}
|
||||
*/
|
||||
async function assertSuccessLink(link, message) {
|
||||
Assert.stringMatches(link.textContent, message);
|
||||
if (message) {
|
||||
let aboutAddonsOpened = BrowserTestUtils.waitForNewTab(
|
||||
gBrowser,
|
||||
"about:addons"
|
||||
);
|
||||
EventUtils.synthesizeMouseAtCenter(link, {}, link.ownerGlobal);
|
||||
let tab = await aboutAddonsOpened;
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +123,7 @@ add_task(async function test_extension_migration_no_matched_extensions() {
|
|||
]);
|
||||
await migration;
|
||||
await wizardDone;
|
||||
assertExtensionsProgressState(
|
||||
await assertExtensionsProgressState(
|
||||
wizard,
|
||||
MigrationWizardConstants.PROGRESS_VALUE.WARNING,
|
||||
{
|
||||
|
@ -149,7 +171,7 @@ add_task(
|
|||
]);
|
||||
await migration;
|
||||
await wizardDone;
|
||||
assertExtensionsProgressState(
|
||||
await assertExtensionsProgressState(
|
||||
wizard,
|
||||
MigrationWizardConstants.PROGRESS_VALUE.INFO,
|
||||
{
|
||||
|
@ -160,7 +182,6 @@ add_task(
|
|||
quantity: TOTAL_EXTENSIONS,
|
||||
}
|
||||
),
|
||||
linkURL: "about:addons",
|
||||
linkText: await gFluentStrings.formatValue(
|
||||
"migration-wizard-progress-extensions-support-link"
|
||||
),
|
||||
|
@ -199,7 +220,7 @@ add_task(async function test_extension_migration_fully_matched_extensions() {
|
|||
]);
|
||||
await migration;
|
||||
await wizardDone;
|
||||
assertExtensionsProgressState(
|
||||
await assertExtensionsProgressState(
|
||||
wizard,
|
||||
MigrationWizardConstants.PROGRESS_VALUE.SUCCESS,
|
||||
{
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[DEFAULT]
|
||||
skip-if = os == 'android'
|
||||
support-files =
|
||||
../head-common.js
|
||||
|
||||
[test_migration_wizard.html]
|
5
browser/components/migration/tests/chrome/chrome.toml
Normal file
5
browser/components/migration/tests/chrome/chrome.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
skip-if = ["os == 'android'"]
|
||||
support-files = ["../head-common.js"]
|
||||
|
||||
["test_migration_wizard.html"]
|
|
@ -592,33 +592,20 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
|||
0
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazy,
|
||||
"optedIn",
|
||||
"browser.shopping.experience2023.optedIn",
|
||||
0
|
||||
);
|
||||
|
||||
let optInDynamicContent;
|
||||
// Limit pref increase to 5 as we don't need to count any higher than that
|
||||
const MIN_VISITS_TO_SHOW_SURVEY = 5;
|
||||
|
||||
class AboutWelcomeShoppingChild extends AboutWelcomeChild {
|
||||
// Static state used to track session in which user opted-in
|
||||
static optedInSession = false;
|
||||
|
||||
actorCreated() {
|
||||
super.actorCreated();
|
||||
this.init();
|
||||
}
|
||||
// Static used to track PDP visits per session for showing survey
|
||||
static eligiblePDPvisits = [];
|
||||
|
||||
init() {
|
||||
// init called on Update Event when child actor created on PDP page open
|
||||
if (!lazy.optedIn) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Limit pref increase to 5 as we don't need to count any higher than that
|
||||
if (lazy.pdpVisits < 5) {
|
||||
computeEligiblePDPCount(data) {
|
||||
// Increment our pref if this isn't a page we've already seen this session
|
||||
if (lazy.pdpVisits < MIN_VISITS_TO_SHOW_SURVEY) {
|
||||
this.AWSendToParent("SPECIAL_ACTION", {
|
||||
type: "SET_PREF",
|
||||
data: {
|
||||
|
@ -630,48 +617,62 @@ class AboutWelcomeShoppingChild extends AboutWelcomeChild {
|
|||
});
|
||||
}
|
||||
|
||||
// Show micro survey to opted-in users at least 24 hours after they’ve opted in,
|
||||
// on the next session, and after 5 PDP visits
|
||||
// Set `this.showMicroSurvey` when above states are met
|
||||
|
||||
// TBD: Wait 24 hrs after opt-in , check if existing shopping logic has when opted-in
|
||||
// else use optInTime pref set when user click opt-in message primary CTA
|
||||
// Add this product to our list of unique eligible PDPs visited
|
||||
// to prevent errors caused by multiple events being fired simultaneously
|
||||
AboutWelcomeShoppingChild.eligiblePDPvisits.push(data?.product_id);
|
||||
}
|
||||
|
||||
evaluateAndShowSurvey() {
|
||||
// Re-evaluate if we should show the survey
|
||||
// Render survey if user is opted-in and has met survey seen conditions
|
||||
this.showMicroSurvey =
|
||||
!lazy.isSurveySeen &&
|
||||
!AboutWelcomeShoppingChild.optedInSession &&
|
||||
lazy.pdpVisits >= MIN_VISITS_TO_SHOW_SURVEY;
|
||||
|
||||
if (this.showMicroSurvey) {
|
||||
this.renderMessage();
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
// Decide when to show/hide onboarding and survey message
|
||||
const { productUrl, showOnboarding } = event.detail;
|
||||
const { productUrl, showOnboarding, data } = event.detail;
|
||||
|
||||
// Display onboarding if a user hasn't opted-in
|
||||
const optInReady = showOnboarding && productUrl;
|
||||
|
||||
// hide the message root if we shouldn't show the opt in card
|
||||
// and if we shouldn't show a microsurvey
|
||||
this.document.getElementById("multi-stage-message-root").hidden =
|
||||
!optInReady && !this.showMicroSurvey;
|
||||
|
||||
// Render survey if user is opted-in and has met survey seen conditions
|
||||
if (!showOnboarding && productUrl && this.showMicroSurvey) {
|
||||
if (optInReady) {
|
||||
// Render opt-in message
|
||||
AboutWelcomeShoppingChild.optedInSession = true;
|
||||
this.AWSetProductURL(new URL(productUrl).hostname);
|
||||
this.renderMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!optInReady) {
|
||||
// Hide the container until the user is eligible to see the survey
|
||||
if (!lazy.isSurveySeen) {
|
||||
this.document.getElementById("multi-stage-message-root").hidden = true;
|
||||
}
|
||||
|
||||
// Early exit if user has seen survey, if we have no data,
|
||||
// or if pdp is ineligible or not unique
|
||||
if (
|
||||
lazy.isSurveySeen ||
|
||||
!data ||
|
||||
!productUrl ||
|
||||
(data?.needs_analysis &&
|
||||
(!data?.product_id || !data?.grade || !data?.adjusted_rating)) ||
|
||||
AboutWelcomeShoppingChild.eligiblePDPvisits.includes(data?.product_id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Render opt-in message
|
||||
AboutWelcomeShoppingChild.optedInSession = true;
|
||||
this.AWSetProductURL(new URL(productUrl).hostname);
|
||||
this.renderMessage();
|
||||
this.computeEligiblePDPCount(data, productUrl);
|
||||
this.evaluateAndShowSurvey();
|
||||
}
|
||||
|
||||
renderMessage() {
|
||||
this.document.getElementById("multi-stage-message-root").hidden = false;
|
||||
this.document.dispatchEvent(
|
||||
new this.contentWindow.CustomEvent("RenderWelcome", {
|
||||
bubbles: true,
|
||||
|
@ -679,6 +680,16 @@ class AboutWelcomeShoppingChild extends AboutWelcomeChild {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO - Move messages into an ASRouter message provider. See bug 1848251.
|
||||
AWGetFeatureConfig() {
|
||||
let messageContent = optInDynamicContent;
|
||||
if (this.showMicroSurvey) {
|
||||
messageContent = SHOPPING_MICROSURVEY;
|
||||
this.setShoppingSurveySeen();
|
||||
}
|
||||
return Cu.cloneInto(messageContent, this.contentWindow);
|
||||
}
|
||||
|
||||
setShoppingSurveySeen() {
|
||||
this.AWSendToParent("SPECIAL_ACTION", {
|
||||
type: "SET_PREF",
|
||||
|
@ -750,15 +761,5 @@ class AboutWelcomeShoppingChild extends AboutWelcomeChild {
|
|||
optInDynamicContent = content;
|
||||
}
|
||||
|
||||
// TODO - Move messages into an ASRouter message provider. See bug 1848251.
|
||||
AWGetFeatureConfig() {
|
||||
let messageContent = optInDynamicContent;
|
||||
if (this.showMicroSurvey) {
|
||||
messageContent = SHOPPING_MICROSURVEY;
|
||||
this.setShoppingSurveySeen();
|
||||
}
|
||||
return Cu.cloneInto(messageContent, this.contentWindow);
|
||||
}
|
||||
|
||||
AWEnsureLangPackInstalled() {}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
"tests/unit/xpcshell.ini",
|
||||
]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.ini"]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.toml"]
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"tests/browser/browser.ini",
|
||||
"tests/browser/interactions/browser.ini",
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
[DEFAULT]
|
||||
support-files = head.js
|
||||
|
||||
[test_0_bug510634.xhtml]
|
||||
[test_bug1163447_selectItems_through_shortcut.xhtml]
|
||||
skip-if = (os == 'win' && processor == 'aarch64') # bug 1532775
|
||||
[test_bug549192.xhtml]
|
||||
[test_bug549491.xhtml]
|
||||
[test_selectItems_on_nested_tree.xhtml]
|
||||
[test_treeview_date.xhtml]
|
17
browser/components/places/tests/chrome/chrome.toml
Normal file
17
browser/components/places/tests/chrome/chrome.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[DEFAULT]
|
||||
support-files = ["head.js"]
|
||||
|
||||
["test_0_bug510634.xhtml"]
|
||||
|
||||
["test_bug1163447_selectItems_through_shortcut.xhtml"]
|
||||
skip-if = [
|
||||
"(os == 'win' && processor == 'aarch64')", # bug 1532775
|
||||
]
|
||||
|
||||
["test_bug549192.xhtml"]
|
||||
|
||||
["test_bug549491.xhtml"]
|
||||
|
||||
["test_selectItems_on_nested_tree.xhtml"]
|
||||
|
||||
["test_treeview_date.xhtml"]
|
|
@ -427,7 +427,7 @@ class TelemetryHandler {
|
|||
adsReported: false,
|
||||
adImpressionsReported: false,
|
||||
impressionId,
|
||||
hrefToComponentMap: null,
|
||||
urlToComponentMap: null,
|
||||
impressionInfo,
|
||||
searchBoxSubmitted: false,
|
||||
});
|
||||
|
@ -440,7 +440,7 @@ class TelemetryHandler {
|
|||
adsReported: false,
|
||||
adImpressionsReported: false,
|
||||
impressionId,
|
||||
hrefToComponentMap: null,
|
||||
urlToComponentMap: null,
|
||||
impressionInfo,
|
||||
searchBoxSubmitted: false,
|
||||
}),
|
||||
|
@ -482,6 +482,71 @@ class TelemetryHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate how close two urls are in equality.
|
||||
*
|
||||
* The scoring system:
|
||||
* - If the URLs look exactly the same, including the ordering of query
|
||||
* parameters, the score is Infinity.
|
||||
* - If the origin is the same, the score is increased by 1. Otherwise the
|
||||
* score is 0.
|
||||
* - If the path is the same, the score is increased by 1.
|
||||
* - For each query parameter, if the key exists the score is increased by 1.
|
||||
* Likewise if the query parameter values match.
|
||||
* - If the hash is the same, the score is increased by 1. This includes if
|
||||
* the hash is missing in both URLs.
|
||||
*
|
||||
* @param {URL} url1
|
||||
* Url to compare.
|
||||
* @param {URL} url2
|
||||
* Other url to compare. Ordering shouldn't matter.
|
||||
* @param {object} [matchOptions]
|
||||
* Options for checking equality.
|
||||
* @param {boolean} [matchOptions.path]
|
||||
* Whether the path must match. Default to false.
|
||||
* @param {boolean} [matchOptions.paramValues]
|
||||
* Whether the values of the query parameters must match if the query
|
||||
* parameter key exists in the other. Defaults to false.
|
||||
* @returns {number}
|
||||
* A score of how closely the two URLs match. Returns 0 if there is no
|
||||
* match or the equality check failed for an enabled match option.
|
||||
*/
|
||||
compareUrls(url1, url2, matchOptions = {}) {
|
||||
// In case of an exact match, well, that's an obvious winner.
|
||||
if (url1.href == url2.href) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
// Each step we get closer to the two URLs being the same, we increase the
|
||||
// score. The consumer of this method will use these scores to see which
|
||||
// of the URLs is the best match.
|
||||
let score = 0;
|
||||
if (url1.origin == url2.origin) {
|
||||
++score;
|
||||
if (url1.pathname == url2.pathname) {
|
||||
++score;
|
||||
for (let [key1, value1] of url1.searchParams) {
|
||||
// Let's not fuss about the ordering of search params, since the
|
||||
// score effect will solve that.
|
||||
if (url2.searchParams.has(key1)) {
|
||||
++score;
|
||||
if (url2.searchParams.get(key1) == value1) {
|
||||
++score;
|
||||
} else if (matchOptions.paramValues) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (url1.hash == url2.hash) {
|
||||
++score;
|
||||
}
|
||||
} else if (matchOptions.path) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parts of the URL, like search params and hashes, may be mutated by scripts
|
||||
* on a page we're tracking. Since we don't want to keep track of that
|
||||
|
@ -506,38 +571,6 @@ class TelemetryHandler {
|
|||
return null;
|
||||
}
|
||||
|
||||
const compareURLs = (url1, url2) => {
|
||||
// In case of an exact match, well, that's an obvious winner.
|
||||
if (url1.href == url2.href) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
// Each step we get closer to the two URLs being the same, we increase the
|
||||
// score. The consumer of this method will use these scores to see which
|
||||
// of the URLs is the best match.
|
||||
let score = 0;
|
||||
if (url1.hostname == url2.hostname) {
|
||||
++score;
|
||||
if (url1.pathname == url2.pathname) {
|
||||
++score;
|
||||
for (let [key1, value1] of url1.searchParams) {
|
||||
// Let's not fuss about the ordering of search params, since the
|
||||
// score effect will solve that.
|
||||
if (url2.searchParams.has(key1)) {
|
||||
++score;
|
||||
if (url2.searchParams.get(key1) == value1) {
|
||||
++score;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (url1.hash == url2.hash) {
|
||||
++score;
|
||||
}
|
||||
}
|
||||
}
|
||||
return score;
|
||||
};
|
||||
|
||||
let item;
|
||||
let currentBestMatch = 0;
|
||||
for (let [trackingURL, candidateItem] of this._browserInfoByURL) {
|
||||
|
@ -553,7 +586,7 @@ class TelemetryHandler {
|
|||
} catch (ex) {
|
||||
continue;
|
||||
}
|
||||
let score = compareURLs(url, trackingURL);
|
||||
let score = this.compareUrls(url, trackingURL);
|
||||
if (score > currentBestMatch) {
|
||||
item = candidateItem;
|
||||
currentBestMatch = score;
|
||||
|
@ -949,7 +982,7 @@ class ContentHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
let URL = wrappedChannel.finalURL;
|
||||
let url = wrappedChannel.finalURL;
|
||||
|
||||
let providerInfo = item.info.provider;
|
||||
let info = this._searchProviderInfo.find(provider => {
|
||||
|
@ -984,7 +1017,7 @@ class ContentHandler {
|
|||
lazy.serpEventsEnabled &&
|
||||
channel.isDocument &&
|
||||
(channel.loadInfo.isTopLevelLoad ||
|
||||
info.nonAdsLinkRegexps.some(r => r.test(URL)))
|
||||
info.nonAdsLinkRegexps.some(r => r.test(url)))
|
||||
) {
|
||||
let browser = wrappedChannel.browserElement;
|
||||
// If the load is from history, don't record an event.
|
||||
|
@ -1033,22 +1066,34 @@ class ContentHandler {
|
|||
isSerp = true;
|
||||
}
|
||||
|
||||
// Determine the "type" of the link.
|
||||
let type = telemetryState.hrefToComponentMap?.get(URL);
|
||||
// The SERP provider may have modified the url with different query
|
||||
// parameters, so try checking all the recorded hrefs to see if any
|
||||
// look similar.
|
||||
if (!type) {
|
||||
for (let [
|
||||
href,
|
||||
componentType,
|
||||
] of telemetryState.hrefToComponentMap.entries()) {
|
||||
if (URL.startsWith(href)) {
|
||||
type = componentType;
|
||||
break;
|
||||
}
|
||||
let startFindComponent = Cu.now();
|
||||
let parsedUrl = new URL(url);
|
||||
// Determine the component type of the link.
|
||||
let type;
|
||||
for (let [
|
||||
storedUrl,
|
||||
componentType,
|
||||
] of telemetryState.urlToComponentMap.entries()) {
|
||||
// The URL we're navigating to may have more query parameters if
|
||||
// the provider adds query parameters when the user clicks on a link.
|
||||
// On the other hand, the URL we are navigating to may have have
|
||||
// fewer query parameters because of query param stripping.
|
||||
// Thus, if a query parameter is missing, a match can still be made
|
||||
// provided keys that exist in both URLs contain equal values.
|
||||
let score = SearchSERPTelemetry.compareUrls(storedUrl, parsedUrl, {
|
||||
paramValues: true,
|
||||
path: true,
|
||||
});
|
||||
if (score) {
|
||||
type = componentType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ChromeUtils.addProfilerMarker(
|
||||
"SearchSERPTelemetry._observeActivity",
|
||||
startFindComponent,
|
||||
"Find component for URL"
|
||||
);
|
||||
|
||||
// Default value for URLs that don't match any components categorized
|
||||
// on the page.
|
||||
|
@ -1082,7 +1127,7 @@ class ContentHandler {
|
|||
lazy.logConsole.debug("Counting click:", {
|
||||
impressionId: telemetryState.impressionId,
|
||||
type,
|
||||
URL,
|
||||
URL: url,
|
||||
});
|
||||
// Prevent re-directed channels from being examined more than once.
|
||||
wrappedChannel._recordedClick = true;
|
||||
|
@ -1094,7 +1139,7 @@ class ContentHandler {
|
|||
);
|
||||
}
|
||||
|
||||
if (!info.extraAdServersRegexps?.some(regex => regex.test(URL))) {
|
||||
if (!info.extraAdServersRegexps?.some(regex => regex.test(url))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1118,7 +1163,7 @@ class ContentHandler {
|
|||
lazy.logConsole.debug("Counting ad click in page for:", {
|
||||
source: item.source,
|
||||
originURL,
|
||||
URL,
|
||||
URL: url,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
@ -1226,7 +1271,13 @@ class ContentHandler {
|
|||
ads_hidden: data.adsHidden,
|
||||
});
|
||||
}
|
||||
telemetryState.hrefToComponentMap = info.hrefToComponentMap;
|
||||
// Convert hrefToComponentMap to a urlToComponentMap in order to cache
|
||||
// the query parameters of the href.
|
||||
let urlToComponentMap = new Map();
|
||||
for (let [href, adType] of info.hrefToComponentMap) {
|
||||
urlToComponentMap.set(new URL(href), adType);
|
||||
}
|
||||
telemetryState.urlToComponentMap = urlToComponentMap;
|
||||
telemetryState.adImpressionsReported = true;
|
||||
Services.obs.notifyObservers(null, "reported-page-with-ad-impressions");
|
||||
}
|
||||
|
|
|
@ -57,6 +57,10 @@ support-files =
|
|||
searchTelemetryAd_searchbox_with_content.html
|
||||
searchTelemetryAd_searchbox_with_content.html^headers^
|
||||
serp.css
|
||||
[browser_search_telemetry_engagement_query_params.js]
|
||||
support-files =
|
||||
searchTelemetryAd_components_query_parameters.html
|
||||
serp.css
|
||||
[browser_search_telemetry_engagement_redirect.js]
|
||||
support-files =
|
||||
redirect_ad.sjs
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests load SERPs and check that query params that are changed either
|
||||
* by the browser or in the page after click are still properly recognized
|
||||
* as ads.
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_PROVIDER_INFO = [
|
||||
{
|
||||
telemetryId: "example",
|
||||
searchPageRegexp:
|
||||
/^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/telemetry\/searchTelemetryAd_/,
|
||||
queryParamName: "s",
|
||||
codeParamName: "abc",
|
||||
taggedCodes: ["ff"],
|
||||
adServerAttributes: ["mozAttr"],
|
||||
nonAdsLinkRegexps: [],
|
||||
extraAdServersRegexps: [/^https:\/\/example\.com\/ad/],
|
||||
components: [
|
||||
{
|
||||
type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
|
||||
included: {
|
||||
parent: {
|
||||
selector: ".moz_ad",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
selector: ".multi-col",
|
||||
type: SearchSERPTelemetryUtils.COMPONENTS.AD_SITELINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
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;
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.search.log", true],
|
||||
["browser.search.serpEventTelemetry.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests();
|
||||
Services.telemetry.canRecordExtended = oldCanRecord;
|
||||
resetTelemetry();
|
||||
});
|
||||
});
|
||||
|
||||
// Baseline test clicking on either link properly categorizes both properly.
|
||||
add_task(async function test_click_links() {
|
||||
let url = getSERPUrl("searchTelemetryAd_components_query_parameters.html");
|
||||
|
||||
info("Load SERP.");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
info("Click on ad link.");
|
||||
let pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"a#ad_link",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
await pageLoadPromise;
|
||||
|
||||
assertImpressionEvents([
|
||||
{
|
||||
impression: {
|
||||
provider: "example",
|
||||
tagged: "true",
|
||||
partner_code: "ff",
|
||||
source: "unknown",
|
||||
is_shopping_page: "false",
|
||||
shopping_tab_displayed: "false",
|
||||
},
|
||||
engagements: [
|
||||
{
|
||||
action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
|
||||
target: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
info("Load SERP again.");
|
||||
await BrowserTestUtils.loadURIString(gBrowser, url);
|
||||
pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
info("Click on site link.");
|
||||
pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"a#ad_sitelink",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
await pageLoadPromise;
|
||||
|
||||
assertImpressionEvents([
|
||||
{
|
||||
impression: {
|
||||
provider: "example",
|
||||
tagged: "true",
|
||||
partner_code: "ff",
|
||||
source: "unknown",
|
||||
is_shopping_page: "false",
|
||||
shopping_tab_displayed: "false",
|
||||
},
|
||||
engagements: [
|
||||
{
|
||||
action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
|
||||
target: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
impression: {
|
||||
provider: "example",
|
||||
tagged: "true",
|
||||
partner_code: "ff",
|
||||
source: "unknown",
|
||||
is_shopping_page: "false",
|
||||
shopping_tab_displayed: "false",
|
||||
},
|
||||
engagements: [
|
||||
{
|
||||
action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
|
||||
target: SearchSERPTelemetryUtils.COMPONENTS.AD_SITELINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
// Clean up.
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_click_link_with_more_parameters() {
|
||||
let url = getSERPUrl("searchTelemetryAd_components_query_parameters.html");
|
||||
|
||||
info("Load SERP.");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
info("After ad impressions, add query parameters to DOM element.");
|
||||
|
||||
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
let el = content.document.getElementById("ad_sitelink");
|
||||
let domUrl = new URL(el.href);
|
||||
domUrl.searchParams.set("example", "param");
|
||||
el.setAttribute("href", domUrl.toString());
|
||||
});
|
||||
|
||||
info("Click on site link.");
|
||||
let pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"a#ad_sitelink",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
await pageLoadPromise;
|
||||
|
||||
assertImpressionEvents([
|
||||
{
|
||||
impression: {
|
||||
provider: "example",
|
||||
tagged: "true",
|
||||
partner_code: "ff",
|
||||
source: "unknown",
|
||||
is_shopping_page: "false",
|
||||
shopping_tab_displayed: "false",
|
||||
},
|
||||
engagements: [
|
||||
{
|
||||
action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
|
||||
target: SearchSERPTelemetryUtils.COMPONENTS.AD_SITELINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
// Clean up.
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_click_link_with_fewer_parameters() {
|
||||
let url = getSERPUrl("searchTelemetryAd_components_query_parameters.html");
|
||||
|
||||
info("Load SERP.");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
info("After ad impressions, remove a query parameter from a DOM element.");
|
||||
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
let el = content.document.getElementById("ad_sitelink");
|
||||
let domUrl = new URL(el.href);
|
||||
domUrl.searchParams.delete("foo");
|
||||
el.setAttribute("href", domUrl.toString());
|
||||
});
|
||||
|
||||
info("Click on site link.");
|
||||
let pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"a#ad_sitelink",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
await pageLoadPromise;
|
||||
|
||||
assertImpressionEvents([
|
||||
{
|
||||
impression: {
|
||||
provider: "example",
|
||||
tagged: "true",
|
||||
partner_code: "ff",
|
||||
source: "unknown",
|
||||
is_shopping_page: "false",
|
||||
shopping_tab_displayed: "false",
|
||||
},
|
||||
engagements: [
|
||||
{
|
||||
action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
|
||||
target: SearchSERPTelemetryUtils.COMPONENTS.AD_SITELINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
// Clean up.
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_click_link_with_reordered_parameters() {
|
||||
let url = getSERPUrl("searchTelemetryAd_components_query_parameters.html");
|
||||
|
||||
info("Load SERP.");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
info("After ad impressions, re-sort the query params.");
|
||||
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
let el = content.document.getElementById("ad_sitelink");
|
||||
let domUrl = new URL(el.href);
|
||||
domUrl.searchParams.sort();
|
||||
el.setAttribute("href", domUrl.toString());
|
||||
});
|
||||
|
||||
info("Click on site link.");
|
||||
let pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"a#ad_sitelink",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
await pageLoadPromise;
|
||||
|
||||
assertImpressionEvents([
|
||||
{
|
||||
impression: {
|
||||
provider: "example",
|
||||
tagged: "true",
|
||||
partner_code: "ff",
|
||||
source: "unknown",
|
||||
is_shopping_page: "false",
|
||||
shopping_tab_displayed: "false",
|
||||
},
|
||||
engagements: [
|
||||
{
|
||||
action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
|
||||
target: SearchSERPTelemetryUtils.COMPONENTS.AD_SITELINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
// Clean up.
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
resetTelemetry();
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="./serp.css" />
|
||||
</head>
|
||||
<body>
|
||||
<section id="searchresults">
|
||||
<div class="lhs">
|
||||
<div class="moz_ad">
|
||||
<h5 test-label>ad_sitelink</h5>
|
||||
<!--
|
||||
Note that the query parameter keys are in reverse alphabetical order
|
||||
that will be reversed in the tests.
|
||||
-->
|
||||
<a id="ad_sitelink" href="https://example.com/ad?foo=bar0&baz=bar0">
|
||||
<h2>Example Result</h2>
|
||||
</a>
|
||||
<div class="multi-col">
|
||||
<div>
|
||||
<a href="https://example.com/ad?foo=bar1&baz=bar1">
|
||||
<h2>New Releases</h2>
|
||||
</a>
|
||||
<span>Cras ac velit sed tellus</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="moz_ad">
|
||||
<h5 test-label>ad_link</h5>
|
||||
<a id="ad_link" href="https://example.com/ad?foo=bar2&baz=bar2">
|
||||
<h2>Example Result</h2>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,190 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* This test ensures we compare URLs correctly. For more info on the scores,
|
||||
* please read the function definition.
|
||||
*/
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
SearchSERPTelemetry: "resource:///modules/SearchSERPTelemetry.sys.mjs",
|
||||
SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
|
||||
});
|
||||
|
||||
const TESTS = [
|
||||
{
|
||||
title: "No difference",
|
||||
url1: "https://www.example.org/search?a=b&c=d#hash",
|
||||
url2: "https://www.example.org/search?a=b&c=d#hash",
|
||||
expected: Infinity,
|
||||
},
|
||||
{
|
||||
// Since the ordering is different, a strict equality match is not going
|
||||
// match. The score will be high, but not Infinity.
|
||||
title: "Different ordering of query parameters",
|
||||
url1: "https://www.example.org/search?c=d&a=b#hash",
|
||||
url2: "https://www.example.org/search?a=b&c=d#hash",
|
||||
expected: 7,
|
||||
},
|
||||
{
|
||||
title: "Different protocol",
|
||||
url1: "http://www.example.org/search",
|
||||
url2: "https://www.example.org/search",
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
title: "Different origin",
|
||||
url1: "https://example.org/search",
|
||||
url2: "https://www.example.org/search",
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
title: "Different path",
|
||||
url1: "https://www.example.org/serp",
|
||||
url2: "https://www.example.org/search",
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
title: "Different path, path on",
|
||||
url1: "https://www.example.org/serp",
|
||||
url2: "https://www.example.org/search",
|
||||
options: {
|
||||
path: true,
|
||||
},
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
title: "Different query parameter keys",
|
||||
url1: "https://www.example.org/search?a=c",
|
||||
url2: "https://www.example.org/search?b=c",
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
title: "Different query parameter keys, paramValues on",
|
||||
url1: "https://www.example.org/search?a=c",
|
||||
url2: "https://www.example.org/search?b=c",
|
||||
options: {
|
||||
paramValues: true,
|
||||
},
|
||||
// Shouldn't change the score because the option should only nullify
|
||||
// the result if one of the keys match but has different values.
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
title: "Some different query parameter keys",
|
||||
url1: "https://www.example.org/search?a=b&c=d",
|
||||
url2: "https://www.example.org/search?a=b",
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
title: "Some different query parameter keys, paramValues on",
|
||||
url1: "https://www.example.org/search?a=b&c=d",
|
||||
url2: "https://www.example.org/search?a=b",
|
||||
options: {
|
||||
paramValues: true,
|
||||
},
|
||||
// Shouldn't change the score because the option should only trigger
|
||||
// if the keys match but values differ.
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
title: "Different query parameter values",
|
||||
url1: "https://www.example.org/search?a=b",
|
||||
url2: "https://www.example.org/search?a=c",
|
||||
expected: 4,
|
||||
},
|
||||
{
|
||||
title: "Different query parameter values, paramValues on",
|
||||
url1: "https://www.example.org/search?a=b&c=d",
|
||||
url2: "https://www.example.org/search?a=b&c=e",
|
||||
options: {
|
||||
paramValues: true,
|
||||
},
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
title: "Some different query parameter values",
|
||||
url1: "https://www.example.org/search?a=b&c=d",
|
||||
url2: "https://www.example.org/search?a=b&c=e",
|
||||
expected: 6,
|
||||
},
|
||||
{
|
||||
title: "Different query parameter values, paramValues on",
|
||||
url1: "https://www.example.org/search?a=b&c=d",
|
||||
url2: "https://www.example.org/search?a=b&c=e",
|
||||
options: {
|
||||
paramValues: true,
|
||||
},
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
title: "Empty query parameter",
|
||||
url1: "https://www.example.org/search?a=b&c",
|
||||
url2: "https://www.example.org/search?c&a=b",
|
||||
expected: 7,
|
||||
},
|
||||
{
|
||||
title: "Empty query parameter, paramValues on",
|
||||
url1: "https://www.example.org/search?a=b&c",
|
||||
url2: "https://www.example.org/search?c&a=b",
|
||||
options: {
|
||||
paramValues: true,
|
||||
},
|
||||
expected: 7,
|
||||
},
|
||||
{
|
||||
title: "Missing empty query parameter",
|
||||
url1: "https://www.example.org/search?c&a=b",
|
||||
url2: "https://www.example.org/search?a=b",
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
title: "Missing empty query parameter, paramValues on",
|
||||
url1: "https://www.example.org/search?c&a=b",
|
||||
url2: "https://www.example.org/search?a=b",
|
||||
options: {
|
||||
paramValues: true,
|
||||
},
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
title: "Different empty query parameter",
|
||||
url1: "https://www.example.org/search?c&a=b",
|
||||
url2: "https://www.example.org/search?a=b&c=foo",
|
||||
expected: 6,
|
||||
},
|
||||
{
|
||||
title: "Different empty query parameter, paramValues on",
|
||||
url1: "https://www.example.org/search?c&a=b",
|
||||
url2: "https://www.example.org/search?a=b&c=foo",
|
||||
options: {
|
||||
paramValues: true,
|
||||
},
|
||||
expected: 0,
|
||||
},
|
||||
];
|
||||
|
||||
add_setup(async function () {
|
||||
Services.prefs.setBoolPref(SearchUtils.BROWSER_SEARCH_PREF + "log", true);
|
||||
await SearchSERPTelemetry.init();
|
||||
});
|
||||
|
||||
add_task(async function test_parsing_extracted_urls() {
|
||||
for (let test of TESTS) {
|
||||
info(test.title);
|
||||
let result = SearchSERPTelemetry.compareUrls(
|
||||
new URL(test.url1),
|
||||
new URL(test.url2),
|
||||
test.options
|
||||
);
|
||||
Assert.equal(result, test.expected, "Equality: url1, url2");
|
||||
|
||||
// Flip the URLs to ensure order doesn't matter.
|
||||
result = SearchSERPTelemetry.compareUrls(
|
||||
new URL(test.url2),
|
||||
new URL(test.url1),
|
||||
test.options
|
||||
);
|
||||
Assert.equal(result, test.expected, "Equality: url2, url1");
|
||||
}
|
||||
});
|
|
@ -3,6 +3,7 @@ skip-if = toolkit == 'android' # bug 1730213
|
|||
firefox-appdir = browser
|
||||
|
||||
[test_search_telemetry_categorization_process_domains.js]
|
||||
[test_search_telemetry_compare_urls.js]
|
||||
[test_search_telemetry_config_validation.js]
|
||||
support-files =
|
||||
../../schema/search-telemetry-schema.json
|
||||
|
|
|
@ -4,21 +4,18 @@
|
|||
ChromeUtils.defineESModuleGetters(this, {
|
||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
ExperimentFakes: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
MockRegistrar: "resource://testing-common/MockRegistrar.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
sinon: "resource://testing-common/Sinon.sys.mjs",
|
||||
});
|
||||
|
||||
const mockedDefaultAgent = {
|
||||
setDefaultBrowserUserChoice: sinon.stub(),
|
||||
setDefaultExtensionHandlersUserChoice: sinon.stub(),
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIDefaultAgent"]),
|
||||
};
|
||||
const setDefaultBrowserUserChoiceStub = sinon.stub();
|
||||
const setDefaultExtensionHandlersUserChoiceStub = sinon.stub();
|
||||
|
||||
const defaultAgentCID = MockRegistrar.register(
|
||||
"@mozilla.org/default-agent;1",
|
||||
mockedDefaultAgent
|
||||
);
|
||||
const defaultAgentStub = sinon.stub(ShellService, "defaultAgent").value({
|
||||
setDefaultBrowserUserChoice: setDefaultBrowserUserChoiceStub,
|
||||
setDefaultExtensionHandlersUserChoice:
|
||||
setDefaultExtensionHandlersUserChoiceStub,
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
|
@ -41,7 +38,7 @@ const shellStub = sinon.stub(ShellService, "shellService").value({
|
|||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
MockRegistrar.unregister(defaultAgentCID);
|
||||
defaultAgentStub.restore();
|
||||
_userChoiceImpossibleTelemetryResultStub.restore();
|
||||
shellStub.restore();
|
||||
|
||||
|
@ -78,15 +75,15 @@ add_task(async function remoteEnableWithPDF() {
|
|||
true
|
||||
);
|
||||
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
ShellService.setDefaultBrowser();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
|
||||
Assert.deepEqual(
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
|
||||
[aumi, [".pdf", "FirefoxPDF"]]
|
||||
);
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(setDefaultBrowserUserChoiceStub.firstCall.args, [
|
||||
aumi,
|
||||
[".pdf", "FirefoxPDF"],
|
||||
]);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
@ -123,12 +120,12 @@ add_task(async function remoteEnableWithPDF_testOnlyReplaceBrowsers() {
|
|||
for (let progId of ["", "MSEdgePDF"]) {
|
||||
queryCurrentDefaultHandlerForStub.callsFake(() => progId);
|
||||
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
|
||||
setDefaultBrowserUserChoiceStub.firstCall.args,
|
||||
[aumi, [".pdf", "FirefoxPDF"]],
|
||||
`Will take default from missing association or known browser with ProgID '${progId}'`
|
||||
);
|
||||
|
@ -137,12 +134,12 @@ add_task(async function remoteEnableWithPDF_testOnlyReplaceBrowsers() {
|
|||
// But not from a non-browser.
|
||||
queryCurrentDefaultHandlerForStub.callsFake(() => "Acrobat.Document.DC");
|
||||
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
|
||||
setDefaultBrowserUserChoiceStub.firstCall.args,
|
||||
[aumi, []],
|
||||
`Will not take default from non-browser`
|
||||
);
|
||||
|
@ -169,15 +166,12 @@ add_task(async function remoteEnableWithoutPDF() {
|
|||
false
|
||||
);
|
||||
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
ShellService.setDefaultBrowser();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
|
||||
Assert.deepEqual(
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
|
||||
[aumi, []]
|
||||
);
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(setDefaultBrowserUserChoiceStub.firstCall.args, [aumi, []]);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
@ -201,10 +195,10 @@ add_task(async function remoteDisable() {
|
|||
true
|
||||
);
|
||||
|
||||
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.notCalled);
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.notCalled);
|
||||
Assert.ok(setDefaultStub.called);
|
||||
|
||||
await doCleanup();
|
||||
|
@ -225,51 +219,51 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
|
|||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.called,
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.firstCall.args,
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.called,
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.firstCall.args,
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
pdfHandlerResult.knownBrowser = false;
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.notCalled,
|
||||
setDefaultExtensionHandlersUserChoiceStub.notCalled,
|
||||
"Did not call default browser agent"
|
||||
);
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.called,
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.firstCall.args,
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
} finally {
|
||||
sandbox.restore();
|
||||
}
|
||||
|
|
|
@ -211,12 +211,31 @@ export class ShoppingSidebarChild extends RemotePageChild {
|
|||
isPolledRequestDone = true;
|
||||
}
|
||||
try {
|
||||
if (!isPolledRequest) {
|
||||
data = await this.#product.requestAnalysis();
|
||||
let analysisStatus;
|
||||
if (isPolledRequest) {
|
||||
// Request a new analysis.
|
||||
let { status } = await this.#product.requestCreateAnalysis();
|
||||
analysisStatus = status;
|
||||
} else {
|
||||
data = await this.#product.pollForAnalysisCompleted();
|
||||
// Check if there is an analysis in progress.
|
||||
let { status } = await this.#product.requestAnalysisCreationStatus();
|
||||
analysisStatus = status;
|
||||
}
|
||||
|
||||
if (
|
||||
analysisStatus &&
|
||||
(analysisStatus == "pending" || analysisStatus == "in_progress")
|
||||
) {
|
||||
// TODO: Send content update to show analysis in progress message,
|
||||
// if not already shown (Bug 1851629).
|
||||
|
||||
await this.#product.pollForAnalysisCompleted({
|
||||
pollInitialWait: analysisStatus == "in_progress" ? 0 : undefined,
|
||||
});
|
||||
isPolledRequestDone = true;
|
||||
}
|
||||
|
||||
data = await this.#product.requestAnalysis();
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch product analysis data", err);
|
||||
data = { error: err };
|
||||
|
@ -225,6 +244,7 @@ export class ShoppingSidebarChild extends RemotePageChild {
|
|||
if (!canContinue(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendToContent("Update", {
|
||||
showOnboarding: false,
|
||||
data,
|
||||
|
@ -232,7 +252,7 @@ export class ShoppingSidebarChild extends RemotePageChild {
|
|||
isPolledRequestDone,
|
||||
});
|
||||
|
||||
if (data.error) {
|
||||
if (!data || data.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -246,9 +266,18 @@ export class ShoppingSidebarChild extends RemotePageChild {
|
|||
);
|
||||
}
|
||||
|
||||
if (!this.canFetchAndShowAd || !this.userHasAdsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#product.requestRecommendations().then(recommendationData => {
|
||||
// Check if the product URI or opt in changed while we waited.
|
||||
if (uri != this.#productURI || !this.canFetchAndShowData) {
|
||||
if (
|
||||
uri != this.#productURI ||
|
||||
!this.canFetchAndShowData ||
|
||||
!this.canFetchAndShowAd ||
|
||||
!this.userHasAdsEnabled
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@ class AnalysisExplainer extends MozLitElement {
|
|||
>
|
||||
<a
|
||||
is="moz-support-link"
|
||||
support-page="todo"
|
||||
support-page="review-checker-review-quality"
|
||||
data-l10n-name="review-quality-url"
|
||||
></a>
|
||||
</p>
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-inline-start: 1px solid var(--in-content-box-border-color);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,11 +71,7 @@ class ShoppingMessageBar extends MozLitElement {
|
|||
></span>
|
||||
<a
|
||||
id="message-bar-reanalysis-link"
|
||||
target="_blank"
|
||||
data-l10n-id="shopping-message-bar-warning-stale-analysis-link"
|
||||
href="https://fakespot.com/analyze?url=${encodeURIComponent(
|
||||
this.productUrl
|
||||
)}"
|
||||
@click=${this.onClickAnalysisLink}
|
||||
></a>
|
||||
</article>
|
||||
|
|
|
@ -10,8 +10,6 @@ import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
|||
// eslint-disable-next-line import/no-unassigned-import
|
||||
import "chrome://browser/content/shopping/shopping-card.mjs";
|
||||
|
||||
import { FAKESPOT_ANALYSIS_URL } from "chrome://global/content/shopping/ProductConfig.mjs";
|
||||
|
||||
class UnanalyzedProductCard extends MozLitElement {
|
||||
static properties = {
|
||||
productURL: { type: String, reflect: true },
|
||||
|
@ -55,10 +53,6 @@ class UnanalyzedProductCard extends MozLitElement {
|
|||
<a
|
||||
id="unanalyzed-product-analysis-link"
|
||||
data-l10n-id="shopping-unanalyzed-product-analyze-link"
|
||||
target="_blank"
|
||||
href="${FAKESPOT_ANALYSIS_URL}${encodeURIComponent(
|
||||
this.productURL
|
||||
)}"
|
||||
@click=${this.onClickAnalysisLink}
|
||||
></a>
|
||||
</div>
|
||||
|
|
|
@ -738,8 +738,8 @@ var TranslationsPanel = new (class {
|
|||
* pertain to languages.
|
||||
*/
|
||||
async #updateSettingsMenuLanguageCheckboxStates() {
|
||||
const { docLangTag, isDocLangTagSupported } =
|
||||
await this.#getCachedDetectedLanguages();
|
||||
const langTags = await this.#getCachedDetectedLanguages();
|
||||
const { docLangTag, isDocLangTagSupported } = langTags;
|
||||
|
||||
const { panel } = this.elements;
|
||||
const alwaysTranslateMenuItems = panel.ownerDocument.querySelectorAll(
|
||||
|
@ -756,7 +756,7 @@ var TranslationsPanel = new (class {
|
|||
const alwaysOfferTranslations =
|
||||
TranslationsParent.shouldAlwaysOfferTranslations();
|
||||
const alwaysTranslateLanguage =
|
||||
TranslationsParent.shouldAlwaysTranslateLanguage(docLangTag);
|
||||
TranslationsParent.shouldAlwaysTranslateLanguage(langTags);
|
||||
const neverTranslateLanguage =
|
||||
TranslationsParent.shouldNeverTranslateLanguage(docLangTag);
|
||||
const shouldDisable =
|
||||
|
@ -1354,14 +1354,15 @@ var TranslationsPanel = new (class {
|
|||
* If auto-translate is currently inactive for the doc language, activates it.
|
||||
*/
|
||||
async onAlwaysTranslateLanguage() {
|
||||
const { docLangTag } = await this.#getCachedDetectedLanguages();
|
||||
const langTags = await this.#getCachedDetectedLanguages();
|
||||
const { docLangTag } = langTags;
|
||||
if (!docLangTag) {
|
||||
throw new Error("Expected to have a document language tag.");
|
||||
}
|
||||
const pageAction =
|
||||
this.getCheckboxPageActionFor().alwaysTranslateLanguage();
|
||||
const toggledOn =
|
||||
TranslationsParent.toggleAlwaysTranslateLanguagePref(docLangTag);
|
||||
TranslationsParent.toggleAlwaysTranslateLanguagePref(langTags);
|
||||
TranslationsParent.telemetry()
|
||||
.panel()
|
||||
.onAlwaysTranslateLanguage(docLangTag, toggledOn);
|
||||
|
|
|
@ -5,6 +5,7 @@ support-files =
|
|||
!/toolkit/components/translations/tests/browser/translations-test.mjs
|
||||
[browser_manage_languages.js]
|
||||
[browser_translations_panel_a11y_focus.js]
|
||||
[browser_translations_panel_always_translate_language_bad_data.js]
|
||||
[browser_translations_panel_always_translate_language_basic.js]
|
||||
[browser_translations_panel_always_translate_language_manual.js]
|
||||
[browser_translations_panel_always_translate_language_restore.js]
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that having an "always translate" set to your app locale doesn't break things.
|
||||
*/
|
||||
add_task(async function test_always_translate_with_bad_data() {
|
||||
const { cleanup, runInPage } = await loadTestPage({
|
||||
page: ENGLISH_PAGE_URL,
|
||||
languagePairs: LANGUAGE_PAIRS,
|
||||
prefs: [["browser.translations.alwaysTranslateLanguages", "en,fr"]],
|
||||
});
|
||||
|
||||
await openTranslationsPanel({
|
||||
onOpenPanel: assertPanelDefaultView,
|
||||
openFromAppMenu: true,
|
||||
});
|
||||
await openTranslationsSettingsMenu();
|
||||
|
||||
await assertIsAlwaysTranslateLanguage("en", {
|
||||
checked: false,
|
||||
disabled: true,
|
||||
});
|
||||
await closeSettingsMenuIfOpen();
|
||||
await closeTranslationsPanelIfOpen();
|
||||
|
||||
info("Checking that the page is untranslated");
|
||||
await runInPage(async TranslationsTest => {
|
||||
const { getH1 } = TranslationsTest.getSelectors();
|
||||
await TranslationsTest.assertTranslationResult(
|
||||
"The page's H1 is untranslated and in the original English.",
|
||||
getH1,
|
||||
'"The Wonderful Wizard of Oz" by L. Frank Baum'
|
||||
);
|
||||
});
|
||||
|
||||
await cleanup();
|
||||
});
|
|
@ -358,6 +358,12 @@ class ProviderQuickSuggest extends UrlbarProvider {
|
|||
result.isRichSuggestion = true;
|
||||
result.richSuggestionIconSize ||= 52;
|
||||
result.suggestedIndex = 1;
|
||||
} else if (
|
||||
suggestion.is_sponsored &&
|
||||
lazy.UrlbarPrefs.get("quickSuggestSponsoredPriority")
|
||||
) {
|
||||
result.isBestMatch = true;
|
||||
result.suggestedIndex = 1;
|
||||
} else if (
|
||||
!isNaN(suggestion.position) &&
|
||||
lazy.UrlbarPrefs.get("quickSuggestAllowPositionInSuggestions")
|
||||
|
|
|
@ -2129,6 +2129,12 @@ export class UrlbarView {
|
|||
return { id: "urlbar-group-mdn" };
|
||||
case "pocket":
|
||||
return { id: "urlbar-group-pocket" };
|
||||
case "adm_sponsored": {
|
||||
if (lazy.UrlbarPrefs.get("quickSuggestSponsoredPriority")) {
|
||||
return { id: "urlbar-group-sponsored" };
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,10 @@ urlbar-group-mdn =
|
|||
urlbar-group-pocket =
|
||||
.label = Recommended reads
|
||||
|
||||
# A label shown above sponsored suggestions in the urlbar results if priority.
|
||||
urlbar-group-sponsored =
|
||||
.label = Sponsored
|
||||
|
||||
# Tooltip text for the block button shown in top pick rows.
|
||||
firefox-suggest-urlbar-block =
|
||||
.title = Dismiss this suggestion
|
||||
|
|
|
@ -198,9 +198,15 @@ export class AdmWikipedia extends BaseFeature {
|
|||
);
|
||||
|
||||
if (suggestion.is_sponsored) {
|
||||
// If quickSuggestSponsoredPriority is enabled, as "Sponsored" label is
|
||||
// shown as group label, no need here.
|
||||
if (!lazy.UrlbarPrefs.get("quickSuggestSponsoredPriority")) {
|
||||
result.payload.descriptionL10n = {
|
||||
id: "urlbar-result-action-sponsored",
|
||||
};
|
||||
result.richSuggestionIconSize = 16;
|
||||
}
|
||||
result.isRichSuggestion = true;
|
||||
result.richSuggestionIconSize = 16;
|
||||
result.payload.descriptionL10n = { id: "urlbar-result-action-sponsored" };
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
prefs=
|
||||
browser.bookmarks.testing.skipDefaultBookmarksImport=true
|
||||
|
||||
[browser_interventions.js]
|
||||
[browser_picks.js]
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
prefs=
|
||||
browser.bookmarks.testing.skipDefaultBookmarksImport=true
|
||||
|
||||
[browser_appendSpanCount.js]
|
||||
[browser_noUpdateResultsFromOtherProviders.js]
|
||||
|
|
|
@ -11,6 +11,7 @@ skip-if =
|
|||
os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
|
||||
|
||||
prefs =
|
||||
browser.bookmarks.testing.skipDefaultBookmarksImport=true
|
||||
browser.urlbar.trending.featureGate=false
|
||||
extensions.screenshots.disabled=false
|
||||
screenshots.browser.component.enabled=true
|
||||
|
|
|
@ -10,6 +10,7 @@ add_setup(async function () {
|
|||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await PlacesUtils.history.clear();
|
||||
await PlacesTestUtils.addVisits(["http://example.com/"]);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
// Disable placeholder completion. The point of this test is to make sure the
|
||||
// first result is autofilled (or not) correctly. Autofilling the placeholder
|
||||
|
|
|
@ -9,6 +9,7 @@ add_task(async function test() {
|
|||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await PlacesUtils.history.clear();
|
||||
await PlacesTestUtils.addVisits(["http://example.com/"]);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
registerCleanupFunction(async () => {
|
||||
await PlacesUtils.history.clear();
|
||||
});
|
||||
|
|
|
@ -884,6 +884,7 @@ async function addVisits(...urls) {
|
|||
await PlacesTestUtils.addVisits(url);
|
||||
}
|
||||
}
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
}
|
||||
|
||||
async function cleanUp() {
|
||||
|
|
|
@ -12,6 +12,7 @@ add_setup(async function () {
|
|||
|
||||
add_task(async function origin() {
|
||||
await PlacesTestUtils.addVisits(["http://example.com/"]);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
// all lowercase
|
||||
await typeAndCheck([
|
||||
["e", "example.com/"],
|
||||
|
@ -48,6 +49,7 @@ add_task(async function origin() {
|
|||
|
||||
add_task(async function url() {
|
||||
await PlacesTestUtils.addVisits(["http://example.com/foo/bar"]);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
// all lowercase
|
||||
await typeAndCheck([
|
||||
["e", "example.com/"],
|
||||
|
|
|
@ -10,6 +10,7 @@ add_task(async function test() {
|
|||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await PlacesUtils.history.clear();
|
||||
await PlacesTestUtils.addVisits(["http://example.com/"]);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
// Search for "ex". It should autofill to example.com/.
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Tests turning non-url-looking values typed in the input field into proper URLs.
|
||||
*/
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
|
||||
|
||||
add_task(async function checkCtrlWorks() {
|
||||
|
@ -61,6 +63,7 @@ add_task(async function checkCtrlWorks() {
|
|||
undefined,
|
||||
true
|
||||
);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await UrlbarTestUtils.inputIntoURLBar(win, inputValue);
|
||||
EventUtils.synthesizeKey("KEY_Enter", options, win);
|
||||
await Promise.all([promiseLoad, promiseStopped]);
|
||||
|
@ -167,17 +170,17 @@ add_task(async function autofill() {
|
|||
["@goo", "https://www.goo.com/", { ctrlKey: true }],
|
||||
];
|
||||
|
||||
function promiseAutofill() {
|
||||
return BrowserTestUtils.waitForEvent(win.gURLBar.inputField, "select");
|
||||
}
|
||||
|
||||
for (let [inputValue, expectedURL, options] of testcases) {
|
||||
let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt(
|
||||
expectedURL,
|
||||
win.gBrowser.selectedBrowser
|
||||
);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
win.gURLBar.select();
|
||||
let autofillPromise = promiseAutofill();
|
||||
let autofillPromise = BrowserTestUtils.waitForEvent(
|
||||
win.gURLBar.inputField,
|
||||
"select"
|
||||
);
|
||||
EventUtils.sendString(inputValue, win);
|
||||
await autofillPromise;
|
||||
EventUtils.synthesizeKey("KEY_Enter", options, win);
|
||||
|
|
|
@ -420,6 +420,7 @@ add_task(async function includingProtocol() {
|
|||
await PlacesTestUtils.clearInputHistory();
|
||||
|
||||
await PlacesTestUtils.addVisits(["https://example.com/"]);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
// If the url is autofilled, the protocol should be included in the copied
|
||||
// value.
|
||||
|
|
|
@ -23,6 +23,7 @@ add_setup(async function () {
|
|||
|
||||
add_task(async function url() {
|
||||
await BrowserTestUtils.withNewTab("http://example.com/", async () => {
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
gURLBar.focus();
|
||||
gURLBar.selectionEnd = gURLBar.untrimmedValue.length;
|
||||
gURLBar.selectionStart = gURLBar.untrimmedValue.length;
|
||||
|
|
|
@ -44,6 +44,7 @@ add_task(async function () {
|
|||
url: "http://example.com/",
|
||||
parentGuid: PlacesUtils.bookmarks.menuGuid,
|
||||
});
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
await SearchTestUtils.installSearchExtension(
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@ add_task(async function bumped() {
|
|||
info("Running subtest: " + JSON.stringify({ url, searchString }));
|
||||
|
||||
await PlacesTestUtils.addVisits(url);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await UrlbarUtils.addToInputHistory(url, input);
|
||||
addToInputHistorySpy.resetHistory();
|
||||
|
||||
|
@ -100,6 +101,7 @@ add_task(async function notBumped_origin() {
|
|||
for (let i = 0; i < 5; i++) {
|
||||
await PlacesTestUtils.addVisits(url);
|
||||
}
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
await triggerAutofillAndPickResult("exam", "example.com/");
|
||||
|
||||
|
@ -120,6 +122,7 @@ add_task(async function notBumped_origin() {
|
|||
add_task(async function notBumped_url() {
|
||||
let url = "http://example.com/test";
|
||||
await PlacesTestUtils.addVisits(url);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
await triggerAutofillAndPickResult("example.com/t", "example.com/test");
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ add_task(async function move_tab_into_new_window_and_open_new_tab() {
|
|||
);
|
||||
let newWindow = gBrowser.replaceTabWithWindow(tab);
|
||||
await swapDocShellPromise;
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
info("Type in the urlbar to open it and see an autofill suggestion.");
|
||||
await UrlbarTestUtils.promisePopupOpen(newWindow, async () => {
|
||||
|
|
|
@ -12,7 +12,7 @@ add_setup(async function () {
|
|||
for (let i = 0; i < 5; i++) {
|
||||
await PlacesTestUtils.addVisits([{ uri: "http://example.com/" }]);
|
||||
}
|
||||
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
|
||||
let defaultEngine = Services.search.getEngineByName("Example");
|
||||
await Services.search.moveEngine(defaultEngine, 0);
|
||||
|
|
|
@ -132,7 +132,7 @@ add_task(async function test_autofill() {
|
|||
let connectionNumber = server.connectionNumber;
|
||||
let searchString = serverInfo.host;
|
||||
info(`Searching for '${searchString}'`);
|
||||
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: searchString,
|
||||
|
@ -162,7 +162,7 @@ add_task(async function test_autofill_privateContext() {
|
|||
let connectionNumber = server.connectionNumber;
|
||||
let searchString = serverInfo.host;
|
||||
info(`Searching for '${searchString}'`);
|
||||
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window: privateWin,
|
||||
value: searchString,
|
||||
|
|
|
@ -36,7 +36,7 @@ add_setup(async function () {
|
|||
for (let i = 0; i < 3; i++) {
|
||||
await PlacesTestUtils.addVisits([`https://${TEST_ENGINE_DOMAIN}/`]);
|
||||
}
|
||||
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
registerCleanupFunction(async () => {
|
||||
await PlacesUtils.history.clear();
|
||||
});
|
||||
|
|
|
@ -366,6 +366,7 @@ const tests = [
|
|||
info("Type an autofilled string, Enter.");
|
||||
win.gURLBar.select();
|
||||
let promise = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window: win,
|
||||
value: "exa",
|
||||
|
@ -837,6 +838,7 @@ const tests = [
|
|||
win.gURLBar.value = "example.org";
|
||||
win.gURLBar.setPageProxyState("invalid");
|
||||
let promise = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await UrlbarTestUtils.promisePopupOpen(win, () => {
|
||||
win.document.getElementById("Browser:OpenLocation").doCommand();
|
||||
});
|
||||
|
@ -866,6 +868,7 @@ const tests = [
|
|||
win.gURLBar.value = "example.com";
|
||||
win.gURLBar.setPageProxyState("invalid");
|
||||
let promise = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await UrlbarTestUtils.promisePopupOpen(win, () => {
|
||||
win.document.getElementById("Browser:OpenLocation").doCommand();
|
||||
});
|
||||
|
|
|
@ -158,18 +158,6 @@ if (AppConstants.platform == "macosx") {
|
|||
}
|
||||
|
||||
add_setup(async function () {
|
||||
// The following initialization code is necessary to avoid a frequent
|
||||
// intermittent failure in verify-fission where, due to timings, we may or
|
||||
// may not import default bookmarks.
|
||||
info("Ensure Places init is complete");
|
||||
let placesInitCompleteObserved = TestUtils.topicObserved(
|
||||
"places-browser-init-complete"
|
||||
);
|
||||
Cc["@mozilla.org/browser/browserglue;1"]
|
||||
.getService(Ci.nsIObserver)
|
||||
.observe(null, "browser-glue-test", "places-browser-init-complete");
|
||||
await placesInitCompleteObserved;
|
||||
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await PlacesUtils.history.clear();
|
||||
await PlacesTestUtils.clearInputHistory();
|
||||
|
@ -294,6 +282,7 @@ add_task(async function history() {
|
|||
for (const { uri, input } of inputHistory) {
|
||||
await UrlbarUtils.addToInputHistory(uri, input);
|
||||
}
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", useAdaptiveHistory);
|
||||
|
||||
|
@ -490,7 +479,7 @@ add_task(async function impression() {
|
|||
await UrlbarUtils.addToInputHistory(uri, input);
|
||||
}
|
||||
}
|
||||
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await triggerAutofillAndPickResult(
|
||||
userInput,
|
||||
autofilled,
|
||||
|
@ -538,6 +527,7 @@ add_task(async function impression() {
|
|||
// Checks autofill deletion telemetry.
|
||||
add_task(async function deletion() {
|
||||
await PlacesTestUtils.addVisits(["http://example.com/"]);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
info("Delete autofilled value by DELETE key");
|
||||
await doDeletionTest({
|
||||
|
|
|
@ -109,6 +109,7 @@ add_task(async function test() {
|
|||
for (let i = 0; i < 3; i++) {
|
||||
await PlacesTestUtils.addVisits([`https://${ENGINE_DOMAIN}/`]);
|
||||
}
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
|
@ -196,6 +197,7 @@ async function impressions_test(isOnboarding) {
|
|||
await PlacesTestUtils.addVisits([`https://${firstEngineHost}-2.com`]);
|
||||
await PlacesTestUtils.addVisits([`https://${ENGINE_DOMAIN}/`]);
|
||||
}
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
// First do multiple searches for substrings of firstEngineHost. The view
|
||||
// should show the same tab-to-search onboarding result the entire time, so
|
||||
|
|
|
@ -18,6 +18,8 @@ support-files =
|
|||
../../ext/schema.json
|
||||
skip-if =
|
||||
os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
|
||||
prefs =
|
||||
browser.bookmarks.testing.skipDefaultBookmarksImport=true
|
||||
|
||||
[browser_glean_telemetry_abandonment_groups.js]
|
||||
[browser_glean_telemetry_abandonment_interaction.js]
|
||||
|
@ -40,6 +42,7 @@ skip-if =
|
|||
[browser_glean_telemetry_engagement_selected_result.js]
|
||||
support-files =
|
||||
../../../../search/test/browser/trendingSuggestionEngine.sjs
|
||||
skip-if = verify # Bug 1852375 - MerinoTestUtils.initWeather() doesn't play well with pushPrefEnv()
|
||||
[browser_glean_telemetry_engagement_tips.js]
|
||||
[browser_glean_telemetry_engagement_type.js]
|
||||
[browser_glean_telemetry_exposure.js]
|
||||
|
|
|
@ -67,6 +67,7 @@ add_task(async function selected_result_autofill_adaptive() {
|
|||
add_task(async function selected_result_autofill_origin() {
|
||||
await doTest(async browser => {
|
||||
await PlacesTestUtils.addVisits("https://example.com/test");
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await openPopup("exa");
|
||||
await doEnter();
|
||||
|
||||
|
@ -85,6 +86,7 @@ add_task(async function selected_result_autofill_origin() {
|
|||
add_task(async function selected_result_autofill_url() {
|
||||
await doTest(async browser => {
|
||||
await PlacesTestUtils.addVisits("https://example.com/test");
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
await openPopup("https://example.com/test");
|
||||
await doEnter();
|
||||
|
||||
|
|
|
@ -35,6 +35,12 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
sinon: "resource://testing-common/Sinon.sys.mjs",
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, "PlacesFrecencyRecalculator", () => {
|
||||
return Cc["@mozilla.org/places/frecency-recalculator;1"].getService(
|
||||
Ci.nsIObserver
|
||||
).wrappedJSObject;
|
||||
});
|
||||
|
||||
async function addTopSites(url) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await PlacesTestUtils.addVisits(url);
|
||||
|
|
|
@ -8,6 +8,8 @@ support-files =
|
|||
../api.js
|
||||
../schema.json
|
||||
head.js
|
||||
prefs =
|
||||
browser.bookmarks.testing.skipDefaultBookmarksImport=true
|
||||
|
||||
[browser_ext_urlbar_attributionURL.js]
|
||||
[browser_ext_urlbar_clearInput.js]
|
||||
|
|
|
@ -423,6 +423,8 @@ class _QuickSuggestTestUtils {
|
|||
* Whether the result is expected to be sponsored.
|
||||
* @param {boolean} [options.isBestMatch]
|
||||
* Whether the result is expected to be a best match.
|
||||
* @param {boolean} [options.isEmptyDescription]
|
||||
* Whether the description text is empty or not.
|
||||
* @returns {result}
|
||||
* The quick suggest result.
|
||||
*/
|
||||
|
@ -433,6 +435,7 @@ class _QuickSuggestTestUtils {
|
|||
index = -1,
|
||||
isSponsored = true,
|
||||
isBestMatch = false,
|
||||
isEmptyDescription = false,
|
||||
} = {}) {
|
||||
this.Assert.ok(
|
||||
url || originalUrl,
|
||||
|
@ -495,7 +498,7 @@ class _QuickSuggestTestUtils {
|
|||
this.Assert.ok(sponsoredElement, "Result sponsored label element exists");
|
||||
this.Assert.equal(
|
||||
sponsoredElement.textContent,
|
||||
isSponsored ? "Sponsored" : "",
|
||||
isSponsored && !isEmptyDescription ? "Sponsored" : "",
|
||||
"Result sponsored label"
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -8,6 +8,8 @@ support-files =
|
|||
searchSuggestionEngine.xml
|
||||
searchSuggestionEngine.sjs
|
||||
subdialog.xhtml
|
||||
prefs =
|
||||
browser.bookmarks.testing.skipDefaultBookmarksImport=true
|
||||
|
||||
[browser_quicksuggest.js]
|
||||
[browser_quicksuggest_addons.js]
|
||||
|
|
|
@ -86,3 +86,77 @@ add_task(async function nonSponsored() {
|
|||
});
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
});
|
||||
|
||||
// Tests sponsored priority feature.
|
||||
add_task(async function sponsoredPriority() {
|
||||
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
|
||||
quickSuggestSponsoredPriority: true,
|
||||
});
|
||||
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: "fra",
|
||||
});
|
||||
await QuickSuggestTestUtils.assertIsQuickSuggest({
|
||||
window,
|
||||
index: 1,
|
||||
isSponsored: true,
|
||||
isBestMatch: true,
|
||||
isEmptyDescription: true,
|
||||
url: `${TEST_URL}?q=frabbits`,
|
||||
});
|
||||
|
||||
let row = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1);
|
||||
Assert.equal(
|
||||
row.querySelector(".urlbarView-title").firstChild.textContent,
|
||||
"fra",
|
||||
"The part of the keyword that matches users input is not bold."
|
||||
);
|
||||
Assert.equal(
|
||||
row.querySelector(".urlbarView-title > strong").textContent,
|
||||
"b",
|
||||
"The auto completed section of the keyword is bolded."
|
||||
);
|
||||
|
||||
// Group label.
|
||||
let before = window.getComputedStyle(row, "::before");
|
||||
Assert.equal(before.content, "attr(label)", "::before.content is enabled");
|
||||
Assert.equal(
|
||||
row.getAttribute("label"),
|
||||
"Sponsored",
|
||||
"Row has 'Sponsored' group label"
|
||||
);
|
||||
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await cleanUpNimbus();
|
||||
});
|
||||
|
||||
// Tests sponsored priority feature does not affect to non-sponsored suggestion.
|
||||
add_task(async function sponsoredPriorityButNotSponsoredSuggestion() {
|
||||
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
|
||||
quickSuggestSponsoredPriority: true,
|
||||
});
|
||||
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: "nonspon",
|
||||
});
|
||||
await QuickSuggestTestUtils.assertIsQuickSuggest({
|
||||
window,
|
||||
index: 1,
|
||||
isSponsored: false,
|
||||
url: `${TEST_URL}?q=nonsponsored`,
|
||||
});
|
||||
|
||||
let row = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1);
|
||||
let before = window.getComputedStyle(row, "::before");
|
||||
Assert.equal(before.content, "attr(label)", "::before.content is enabled");
|
||||
Assert.equal(
|
||||
row.getAttribute("label"),
|
||||
"Firefox Suggest",
|
||||
"Row has general group label for quick suggest"
|
||||
);
|
||||
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await cleanUpNimbus();
|
||||
});
|
||||
|
|
|
@ -294,6 +294,7 @@ async function doTest({
|
|||
recordNavigationalSuggestionTelemetry: true,
|
||||
},
|
||||
}) {
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
MerinoTestUtils.server.response.body.suggestions = suggestion
|
||||
? [suggestion]
|
||||
: [];
|
||||
|
|
|
@ -35,6 +35,12 @@ ChromeUtils.defineLazyGetter(this, "MerinoTestUtils", () => {
|
|||
return module;
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, "PlacesFrecencyRecalculator", () => {
|
||||
return Cc["@mozilla.org/places/frecency-recalculator;1"].getService(
|
||||
Ci.nsIObserver
|
||||
).wrappedJSObject;
|
||||
});
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
// Ensure the popup is always closed at the end of each test to avoid
|
||||
// interfering with the next test.
|
||||
|
|
|
@ -1373,3 +1373,86 @@ add_task(async function remoteSettingsDataType() {
|
|||
await cleanUpNimbus();
|
||||
}
|
||||
});
|
||||
|
||||
// For priority sponsored suggestion,
|
||||
// always isBestMatch will be true and suggestIndex will be 1.
|
||||
// It does not have descriptionL10n.
|
||||
const EXPECTED_SPONSORED_PRIORITY_RESULT = {
|
||||
...EXPECTED_SPONSORED_RESULT,
|
||||
isBestMatch: true,
|
||||
payload: {
|
||||
...EXPECTED_SPONSORED_RESULT.payload,
|
||||
descriptionL10n: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
add_task(async function sponsoredPriority_normal() {
|
||||
await doSponsoredPriorityTest({
|
||||
searchWord: SPONSORED_SEARCH_STRING,
|
||||
remoteSettingsData: [REMOTE_SETTINGS_RESULTS[0]],
|
||||
expectedMatches: [EXPECTED_SPONSORED_PRIORITY_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function sponsoredPriority_nonsponsoredSuggestion() {
|
||||
// Not affect to except sponsored suggestion.
|
||||
await doSponsoredPriorityTest({
|
||||
searchWord: NONSPONSORED_SEARCH_STRING,
|
||||
remoteSettingsData: [REMOTE_SETTINGS_RESULTS[1]],
|
||||
expectedMatches: [EXPECTED_NONSPONSORED_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function sponsoredPriority_sponsoredIndex() {
|
||||
await doSponsoredPriorityTest({
|
||||
nimbusSettings: { quickSuggestSponsoredIndex: 2 },
|
||||
searchWord: SPONSORED_SEARCH_STRING,
|
||||
remoteSettingsData: [REMOTE_SETTINGS_RESULTS[0]],
|
||||
expectedMatches: [EXPECTED_SPONSORED_PRIORITY_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function sponsoredPriority_position() {
|
||||
await doSponsoredPriorityTest({
|
||||
nimbusSettings: { quickSuggestAllowPositionInSuggestions: true },
|
||||
searchWord: SPONSORED_SEARCH_STRING,
|
||||
remoteSettingsData: [
|
||||
Object.assign({}, REMOTE_SETTINGS_RESULTS[0], { position: 2 }),
|
||||
],
|
||||
expectedMatches: [EXPECTED_SPONSORED_PRIORITY_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
async function doSponsoredPriorityTest({
|
||||
remoteSettingsConfig = {},
|
||||
nimbusSettings = {},
|
||||
searchWord,
|
||||
remoteSettingsData,
|
||||
expectedMatches,
|
||||
}) {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
|
||||
const cleanUpNimbusEnable = await UrlbarTestUtils.initNimbusFeature({
|
||||
...nimbusSettings,
|
||||
quickSuggestSponsoredPriority: true,
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "data",
|
||||
attachment: remoteSettingsData,
|
||||
},
|
||||
]);
|
||||
await QuickSuggestTestUtils.setConfig(remoteSettingsConfig);
|
||||
|
||||
await check_results({
|
||||
context: createContext(searchWord, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
}),
|
||||
matches: expectedMatches,
|
||||
});
|
||||
|
||||
await cleanUpNimbusEnable();
|
||||
}
|
||||
|
|
|
@ -872,7 +872,7 @@ async function check_results({
|
|||
// updates.
|
||||
// This is not a problem in real life, but autocomplete tests should
|
||||
// return reliable resultsets, thus we have to wait.
|
||||
await PlacesTestUtils.promiseAsyncUpdates();
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
|
||||
const controller = UrlbarTestUtils.newMockController({
|
||||
input: {
|
||||
|
|
|
@ -30,6 +30,7 @@ add_task(async function () {
|
|||
}
|
||||
|
||||
async function check_autofill() {
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
let threshold = await getOriginAutofillThreshold();
|
||||
let httpOriginFrecency = await getOriginFrecency("http://", host);
|
||||
Assert.less(
|
||||
|
|
|
@ -1025,7 +1025,7 @@ async function doTitleTest({ visits, input, expected }) {
|
|||
url: uri,
|
||||
}
|
||||
);
|
||||
await db.executeCached("DELETE FROM moz_updateoriginsupdate_temp");
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -946,7 +946,7 @@ add_autofill_task(async function zeroThreshold() {
|
|||
await db.execute("UPDATE moz_places SET frecency = -1 WHERE url = :url", {
|
||||
url: pageUrl,
|
||||
});
|
||||
await db.executeCached("DELETE FROM moz_updateoriginsupdate_temp");
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
});
|
||||
|
||||
// Make sure the place's frecency is -1.
|
||||
|
@ -1120,14 +1120,14 @@ add_autofill_task(async function suggestHistoryFalse_bookmark_0() {
|
|||
// Make the bookmark fall below the autofill frecency threshold so we ensure
|
||||
// the bookmark is always autofilled in this case, even if it doesn't meet
|
||||
// the threshold.
|
||||
let meetsThreshold = true;
|
||||
while (meetsThreshold) {
|
||||
await TestUtils.waitForCondition(async () => {
|
||||
// Add a visit to another origin to boost the threshold.
|
||||
await PlacesTestUtils.addVisits("http://foo-" + url);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
let originFrecency = await getOriginFrecency("http://", host);
|
||||
let threshold = await getOriginAutofillThreshold();
|
||||
meetsThreshold = threshold <= originFrecency;
|
||||
}
|
||||
return threshold > originFrecency;
|
||||
}, "Make the bookmark fall below the frecency threshold");
|
||||
|
||||
// At this point, the bookmark doesn't meet the threshold, but it should
|
||||
// still be autofilled.
|
||||
|
@ -1227,14 +1227,14 @@ add_autofill_task(async function suggestHistoryFalse_bookmark_prefix_0() {
|
|||
// Make the bookmark fall below the autofill frecency threshold so we ensure
|
||||
// the bookmark is always autofilled in this case, even if it doesn't meet
|
||||
// the threshold.
|
||||
let meetsThreshold = true;
|
||||
while (meetsThreshold) {
|
||||
await TestUtils.waitForCondition(async () => {
|
||||
// Add a visit to another origin to boost the threshold.
|
||||
await PlacesTestUtils.addVisits("http://foo-" + url);
|
||||
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
|
||||
let originFrecency = await getOriginFrecency("http://", host);
|
||||
let threshold = await getOriginAutofillThreshold();
|
||||
meetsThreshold = threshold <= originFrecency;
|
||||
}
|
||||
return threshold > originFrecency;
|
||||
}, "Make the bookmark fall below the frecency threshold");
|
||||
|
||||
// At this point, the bookmark doesn't meet the threshold, but it should
|
||||
// still be autofilled.
|
||||
|
|
|
@ -74,6 +74,7 @@ add_task(
|
|||
false,
|
||||
"Search Service should have failed to initialize."
|
||||
);
|
||||
await cleanupPlaces();
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -256,6 +256,12 @@ let AVAILABLE_PIP_OVERRIDES;
|
|||
},
|
||||
},
|
||||
|
||||
viki: {
|
||||
"https://*.viki.com/*": {
|
||||
videoWrapperScriptPath: "video-wrappers/videojsWrapper.js",
|
||||
},
|
||||
},
|
||||
|
||||
voot: {
|
||||
"https://*.voot.com/*": {
|
||||
videoWrapperScriptPath: "video-wrappers/voot.js",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue