зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1835321 - Allow any link on SERPs that don't match an ad expression to be considered a non-ad - r=Standard8
Differential Revision: https://phabricator.services.mozilla.com/D179367
This commit is contained in:
Родитель
f31d0a6ec2
Коммит
b7663bfe8c
|
@ -67,9 +67,6 @@ class SearchProviders {
|
|||
extraAdServersRegexps: p.extraAdServersRegexps.map(
|
||||
r => new RegExp(r)
|
||||
),
|
||||
nonAdsLinkRegexps: p.nonAdsLinkRegexps?.length
|
||||
? p.nonAdsLinkRegexps.map(r => new RegExp(r))
|
||||
: [],
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -388,13 +385,6 @@ class SearchAdImpression {
|
|||
if (result.relatedElements?.length) {
|
||||
this.#addEventListenerToElements(result.relatedElements, result.type);
|
||||
}
|
||||
// If an anchor doesn't match any component, and it doesn't have a non
|
||||
// ads link regexp, cache the anchor so the parent process can observe
|
||||
// them.
|
||||
} else if (!this.#providerInfo.nonAdsLinkRegexps.length) {
|
||||
this.#recordElementData(anchor, {
|
||||
type: "non_ads_link",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -925,9 +925,16 @@ class ContentHandler {
|
|||
}
|
||||
|
||||
let wrappedChannel = ChannelWrapper.get(channel);
|
||||
// The channel we're observing might be a redirect of a channel we've
|
||||
// observed before.
|
||||
if (wrappedChannel._adClickRecorded) {
|
||||
lazy.logConsole.debug("Ad click already recorded");
|
||||
return;
|
||||
// When _adClickRecorded is false but _recordedClick is true, it means we
|
||||
// recorded a non-ad link click, and it is being re-directed.
|
||||
} else if (wrappedChannel._recordedClick) {
|
||||
lazy.logConsole.debug("Non ad-click already recorded");
|
||||
return;
|
||||
}
|
||||
|
||||
Services.tm.dispatchToMainThread(() => {
|
||||
|
@ -953,15 +960,35 @@ class ContentHandler {
|
|||
return provider.telemetryId == providerInfo;
|
||||
});
|
||||
|
||||
// The SERP "clicked" action is implied if a user loads another page from
|
||||
// the context of a SERP. At this point, we don't know if the request is
|
||||
// from a SERP but we want to avoid inspecting requests that are not
|
||||
// documents, or not a top level load.
|
||||
// Some channels re-direct by loading pages that return 200. The result
|
||||
// is the channel will have an originURL that changes from the SERP to
|
||||
// either a nonAdsRegexp or an extraAdServersRegexps. This is typical
|
||||
// for loading a page in a new tab. The channel will have changed so any
|
||||
// properties attached to them to record state (e.g. _recordedClick)
|
||||
// won't be present.
|
||||
if (
|
||||
info.nonAdsLinkRegexps.some(r => r.test(originURL)) ||
|
||||
info.extraAdServersRegexps.some(r => r.test(originURL))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A click event is recorded if a user loads a resource from an
|
||||
// originURL that is a SERP.
|
||||
//
|
||||
// Typically, we only want top level loads containing documents to avoid
|
||||
// recording any event on an in-page resource a SERP might load
|
||||
// (e.g. CSS files).
|
||||
//
|
||||
// The exception to this is if a subframe loads a resource that matches
|
||||
// a non ad link. Some SERPs encode non ad search results with a URL
|
||||
// that gets loaded into an iframe, which then tells the container of
|
||||
// the iframe to change the location of the page.
|
||||
if (
|
||||
lazy.serpEventsEnabled &&
|
||||
channel.isDocument &&
|
||||
channel.loadInfo.isTopLevelLoad &&
|
||||
!wrappedChannel._countedClick
|
||||
(channel.loadInfo.isTopLevelLoad ||
|
||||
info.nonAdsLinkRegexps.some(r => r.test(URL)))
|
||||
) {
|
||||
let start = Cu.now();
|
||||
|
||||
|
@ -1018,27 +1045,12 @@ class ContentHandler {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Check if the href matches a non-ads link. Do this after looking at
|
||||
// hrefToComponentMap because a link that looks like a non-ad might
|
||||
// have a more specific component type.
|
||||
|
||||
// Default value for URLs that don't match any components categorized
|
||||
// on the page.
|
||||
if (!type) {
|
||||
type = info.nonAdsLinkRegexps.some(r => r.test(URL))
|
||||
? SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK
|
||||
: "";
|
||||
}
|
||||
// The SERP may have moved onto another page that matches a SERP page
|
||||
// e.g. Related Search
|
||||
if (!type && isSerp) {
|
||||
type = SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK;
|
||||
}
|
||||
// There might be other types of pages on a SERP that don't fall
|
||||
// neatly into expected non-ad expressions or SERPs, such as Image
|
||||
// Search, Maps, etc.
|
||||
if (!type) {
|
||||
type = info.extraPageRegexps?.some(r => r.test(URL))
|
||||
? SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK
|
||||
: "";
|
||||
}
|
||||
|
||||
if (isSerp && isFromNewtab) {
|
||||
SearchSERPTelemetry.setBrowserContentSource(
|
||||
|
@ -1047,18 +1059,7 @@ class ContentHandler {
|
|||
);
|
||||
}
|
||||
|
||||
// Step 3: If we have a type, record an engagement.
|
||||
// Exceptions:
|
||||
// - Related searches on some SERPs can be encoded with a URL that
|
||||
// match a nonAdsLinkRegexp. This means we'll have seen the link
|
||||
// twice, once with the nonAdsLinkRegexp and again with a SERP URL
|
||||
// matching a searchPageRegexp. We don't want to record the
|
||||
// engagement twice, so if the origin of the request was
|
||||
// nonAdsLinkRegexp, skip the categorization. The reason why we
|
||||
// don't do this check earlier is because if the final URL is a
|
||||
// SERP, we'll want to define the source property of the subsequent
|
||||
// SERP impression.
|
||||
if (type && !info.nonAdsLinkRegexps.some(r => r.test(originURL))) {
|
||||
// Step 3: Record the engagement.
|
||||
impressionIdsWithoutEngagementsSet.delete(
|
||||
telemetryState.impressionId
|
||||
);
|
||||
|
@ -1072,10 +1073,8 @@ class ContentHandler {
|
|||
type,
|
||||
URL,
|
||||
});
|
||||
wrappedChannel._countedClick = true;
|
||||
} else if (!type) {
|
||||
lazy.logConsole.warn(`Could not find a component type for ${URL}`);
|
||||
}
|
||||
// Prevent re-directed channels from being examined more than once.
|
||||
wrappedChannel._recordedClick = true;
|
||||
}
|
||||
ChromeUtils.addProfilerMarker(
|
||||
"SearchSERPTelemetry._observeActivity",
|
||||
|
|
|
@ -99,13 +99,30 @@ tags = search-telemetry
|
|||
support-files =
|
||||
searchTelemetryAd_searchbox_with_content.html
|
||||
searchTelemetryAd_searchbox_with_content.html^headers^
|
||||
[browser_search_telemetry_engagement_non_ad.js]
|
||||
tags = search-telemetry
|
||||
support-files =
|
||||
searchTelemetryAd_searchbox_with_content.html
|
||||
searchTelemetryAd_searchbox_with_content.html^headers^
|
||||
serp.css
|
||||
[browser_search_telemetry_engagement_redirect.js]
|
||||
tags = search-telemetry
|
||||
support-files =
|
||||
redirect_final.sjs
|
||||
redirect_once.sjs
|
||||
redirect_thrice.sjs
|
||||
redirect_twice.sjs
|
||||
searchTelemetryAd_adsLink_redirect.html
|
||||
searchTelemetryAd_components_text.html
|
||||
searchTelemetryAd_nonAdsLink_redirect.html
|
||||
searchTelemetryAd_nonAdsLink_redirect.html^headers^
|
||||
searchTelemetryAd_nonAdsLink_redirect_nonTopLoaded.html
|
||||
searchTelemetryAd_nonAdsLink_redirect_nonTopLoaded.html^headers^
|
||||
serp.css
|
||||
[browser_search_telemetry_engagement_target.js]
|
||||
tags = search-telemetry
|
||||
support-files =
|
||||
searchTelemetryAd_components_text.html
|
||||
searchTelemetryAd_nonAdsLink_redirect.html
|
||||
searchTelemetryAd_nonAdsLink_redirect.html^headers^
|
||||
searchTelemetryAd_searchbox.html
|
||||
searchTelemetryAd_searchbox.html^headers^
|
||||
serp.css
|
||||
|
|
|
@ -19,9 +19,7 @@ const TEST_PROVIDER_INFO = [
|
|||
codeParamName: "abc",
|
||||
taggedCodes: ["ff"],
|
||||
adServerAttributes: ["mozAttr"],
|
||||
nonAdsLinkRegexps: [
|
||||
/^https:\/\/example.com\/browser\/browser\/components\/search\/test\/browser\//,
|
||||
],
|
||||
nonAdsLinkRegexps: [],
|
||||
extraAdServersRegexps: [/^https:\/\/example\.com\/ad/],
|
||||
components: [
|
||||
{
|
||||
|
|
|
@ -323,11 +323,8 @@ add_task(async function test_click_related_search_in_new_tab() {
|
|||
BrowserTestUtils.removeTab(tab2);
|
||||
});
|
||||
|
||||
// We consider regular expressions in nonAdsLinkRegexps and
|
||||
// searchPageRegexp/extraPageRegexps as valid non ads links when recording
|
||||
// an engagement event. However, if a nonAdsLinkRegexp leads to a
|
||||
// searchPageRegexp/extraPageRegexps, than we risk double counting in the case
|
||||
// of a re-direct occuring in a new tab.
|
||||
// We consider regular expressions in nonAdsLinkRegexps and searchPageRegexp
|
||||
// as valid non ads links when recording an engagement event.
|
||||
add_task(async function test_click_redirect_search_in_newtab() {
|
||||
resetTelemetry();
|
||||
let url = getSERPUrl("searchTelemetryAd_searchbox_with_content.html");
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests load SERPs and click on links that are non ads. Non ads can have
|
||||
* slightly different behavior from ads.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { SearchSERPTelemetry, SearchSERPTelemetryUtils } =
|
||||
ChromeUtils.importESModule("resource:///modules/SearchSERPTelemetry.sys.mjs");
|
||||
|
||||
const TEST_PROVIDER_INFO = [
|
||||
{
|
||||
telemetryId: "example",
|
||||
searchPageRegexp:
|
||||
/^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_/,
|
||||
queryParamName: "s",
|
||||
codeParamName: "abc",
|
||||
taggedCodes: ["ff"],
|
||||
adServerAttributes: ["mozAttr"],
|
||||
nonAdsLinkRegexps: [],
|
||||
extraAdServersRegexps: [/^https:\/\/example\.com\/ad/],
|
||||
components: [
|
||||
{
|
||||
type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function getSERPUrl(page, organic = false) {
|
||||
let url =
|
||||
getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content",
|
||||
"https://example.org"
|
||||
) + page;
|
||||
return `${url}?s=test${organic ? "" : "&abc=ff"}`;
|
||||
}
|
||||
|
||||
async function promiseImpressionReceived() {
|
||||
return TestUtils.waitForCondition(() => {
|
||||
let adImpressions = Glean.serp.adImpression.testGetValue() ?? [];
|
||||
return adImpressions.length;
|
||||
}, "Should have received an ad impression.");
|
||||
}
|
||||
|
||||
async function waitForIdle() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await new Promise(resolve => Services.tm.idleDispatchToMainThread(resolve));
|
||||
}
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
|
||||
await waitForIdle();
|
||||
// Enable local telemetry recording for the duration of the tests.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.search.log", true],
|
||||
["browser.search.serpEventTelemetry.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests();
|
||||
resetTelemetry();
|
||||
});
|
||||
});
|
||||
|
||||
// If an anchor is a non_ads_link and it doesn't match a non-ads regular
|
||||
// expression, it should still be categorize it as a non ad.
|
||||
add_task(async function test_click_non_ads_link() {
|
||||
await waitForIdle();
|
||||
|
||||
resetTelemetry();
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
// Click a non ad.
|
||||
let pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#non_ads_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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// Reset state for other tests.
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
|
||||
await waitForIdle();
|
||||
});
|
||||
|
||||
// Click on an non-ad element while no ads are present.
|
||||
add_task(async function test_click_non_ad_with_no_ads() {
|
||||
await waitForIdle();
|
||||
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_searchbox.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
"https://example.com/hello_world"
|
||||
);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#non_ads_link",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
await browserLoadedPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// Reset state for other tests.
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
|
||||
await waitForIdle();
|
||||
});
|
|
@ -0,0 +1,346 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests load SERPs and click on both ad and non-ad links that can be
|
||||
* redirected.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { SearchSERPTelemetry, SearchSERPTelemetryUtils } =
|
||||
ChromeUtils.importESModule("resource:///modules/SearchSERPTelemetry.sys.mjs");
|
||||
|
||||
const TEST_PROVIDER_INFO = [
|
||||
{
|
||||
telemetryId: "example",
|
||||
searchPageRegexp:
|
||||
/^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_/,
|
||||
queryParamName: "s",
|
||||
codeParamName: "abc",
|
||||
taggedCodes: ["ff"],
|
||||
adServerAttributes: ["mozAttr"],
|
||||
nonAdsLinkRegexps: [
|
||||
/^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_nonAdsLink_redirect.html/,
|
||||
],
|
||||
extraAdServersRegexps: [
|
||||
/^https:\/\/example\.com\/ad/,
|
||||
/^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_adsLink_redirect.html/,
|
||||
],
|
||||
components: [
|
||||
{
|
||||
type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function getSERPUrl(page, organic = false) {
|
||||
let url =
|
||||
getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content",
|
||||
"https://example.org"
|
||||
) + page;
|
||||
return `${url}?s=test${organic ? "" : "&abc=ff"}`;
|
||||
}
|
||||
|
||||
async function promiseImpressionReceived() {
|
||||
return TestUtils.waitForCondition(() => {
|
||||
let adImpressions = Glean.serp.adImpression.testGetValue() ?? [];
|
||||
return adImpressions.length;
|
||||
}, "Should have received an ad impression.");
|
||||
}
|
||||
|
||||
async function waitForIdle() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await new Promise(resolve => Services.tm.idleDispatchToMainThread(resolve));
|
||||
}
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
|
||||
await waitForIdle();
|
||||
// Enable local telemetry recording for the duration of the tests.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.search.log", true],
|
||||
["browser.search.serpEventTelemetry.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests();
|
||||
resetTelemetry();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_click_non_ads_link_redirected() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
targetUrl
|
||||
);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#non_ads_link_redirected",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
|
||||
await browserLoadedPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// If a provider does a re-direct and we open it in a new tab, we should
|
||||
// record the click and have the correct number of engagements.
|
||||
add_task(async function test_click_non_ads_link_redirected_new_tab() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let redirectUrl =
|
||||
getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content",
|
||||
"https://example.org"
|
||||
) + "searchTelemetryAd_nonAdsLink_redirect.html";
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, targetUrl, true);
|
||||
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [redirectUrl], urls => {
|
||||
content.document
|
||||
.getElementById(["non_ads_link"])
|
||||
.addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
content.window.open([urls], "_blank");
|
||||
});
|
||||
content.document.getElementById("non_ads_link").click();
|
||||
});
|
||||
let tab2 = await tabPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
BrowserTestUtils.removeTab(tab2);
|
||||
});
|
||||
|
||||
// Some providers load a URL of a non ad within a subframe before loading the
|
||||
// target website in the top level frame.
|
||||
add_task(async function test_click_non_ads_link_redirect_non_top_level() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let browserPromise = BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
targetUrl
|
||||
);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#non_ads_link_redirected_no_top_level",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
|
||||
await browserPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_multiple_redirects_non_ad_link() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
targetUrl
|
||||
);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#non_ads_link_multiple_redirects",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
|
||||
await browserLoadedPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_click_ad_link_redirected() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
targetUrl
|
||||
);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#ad_link_redirect",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
|
||||
await browserLoadedPromise;
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_click_ad_link_redirected_new_tab() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, targetUrl, true);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#ad_link_redirect",
|
||||
{ button: 1 },
|
||||
tab.linkedBrowser
|
||||
);
|
||||
let tab2 = await tabPromise;
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
BrowserTestUtils.removeTab(tab2);
|
||||
});
|
|
@ -22,7 +22,7 @@ const TEST_PROVIDER_INFO = [
|
|||
taggedCodes: ["ff"],
|
||||
adServerAttributes: ["mozAttr"],
|
||||
nonAdsLinkRegexps: [
|
||||
/^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_nonAdsLink_redirect/,
|
||||
/^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_nonAdsLink_redirect.html/,
|
||||
],
|
||||
extraAdServersRegexps: [/^https:\/\/example\.com\/ad/],
|
||||
components: [
|
||||
|
@ -190,112 +190,6 @@ add_task(async function test_click_second_ad_in_component() {
|
|||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// If a provider does a re-direct and we open it in a new tab, we should
|
||||
// record the click and have the correct number of engagements.
|
||||
add_task(async function test_click_non_ads_link_redirected_new_tab() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let redirectUrl =
|
||||
getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content",
|
||||
"https://example.org"
|
||||
) + "searchTelemetryAd_nonAdsLink_redirect.html";
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, targetUrl, true);
|
||||
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [redirectUrl], urls => {
|
||||
content.document
|
||||
.getElementById(["non_ads_link"])
|
||||
.addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
content.window.open([urls], "_blank");
|
||||
});
|
||||
content.document.getElementById("non_ads_link").click();
|
||||
});
|
||||
let tab2 = await tabPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab2);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// If a provider does a re-direct in the same tab, we may not be able to
|
||||
// determine the component based on the URLs on the page, because the initial
|
||||
// redirect URL won't be known, and by the time the page is loaded, the cached
|
||||
// data might be invalidated. So this should use regular expressions to make
|
||||
// an inference.
|
||||
add_task(async function test_click_non_ads_link_redirected() {
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let redirectUrl =
|
||||
getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content",
|
||||
"https://example.org"
|
||||
) + "searchTelemetryAd_nonAdsLink_redirect.html";
|
||||
let targetUrl = "https://example.com/hello_world";
|
||||
|
||||
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
targetUrl
|
||||
);
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [redirectUrl], urls => {
|
||||
content.document
|
||||
.getElementById(["non_ads_link"])
|
||||
.setAttribute("href", [urls]);
|
||||
content.document.getElementById("non_ads_link").click();
|
||||
});
|
||||
await browserLoadedPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// If a provider appends query parameters to a link after the page has been
|
||||
// parsed, we should still be able to record the click.
|
||||
add_task(async function test_click_ads_link_modified() {
|
||||
|
@ -337,54 +231,6 @@ add_task(async function test_click_ads_link_modified() {
|
|||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// If an anchor is a non_ads_link and we don't have a matching regular
|
||||
// expression, we should still be categorize it as non ads.
|
||||
add_task(async function test_click_non_ads_link() {
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(
|
||||
TEST_PROVIDER_INFO_NO_NON_ADS_REGEXP
|
||||
);
|
||||
await waitForIdle();
|
||||
|
||||
resetTelemetry();
|
||||
let url = getSERPUrl("searchTelemetryAd_components_text.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
// Click a non ad.
|
||||
let pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#non_ads_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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// Reset state for other tests.
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
|
||||
await waitForIdle();
|
||||
});
|
||||
|
||||
// Search box is a special case which has to be tracked in the child process.
|
||||
add_task(async function test_click_and_submit_incontent_searchbox() {
|
||||
resetTelemetry();
|
||||
|
@ -531,58 +377,6 @@ add_task(async function test_click_carousel_expand() {
|
|||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// Click on an non-ad element while no ads are present.
|
||||
add_task(async function test_click_non_ad_with_no_ads() {
|
||||
// Use a provider that doesn't a stored non-ads regexp.
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(
|
||||
TEST_PROVIDER_INFO_NO_NON_ADS_REGEXP
|
||||
);
|
||||
await waitForIdle();
|
||||
|
||||
resetTelemetry();
|
||||
|
||||
let url = getSERPUrl("searchTelemetryAd_searchbox.html");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
await waitForPageWithAdImpressions();
|
||||
|
||||
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
"https://example.com/hello_world"
|
||||
);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#non_ads_link",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
await browserLoadedPromise;
|
||||
|
||||
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.NON_ADS_LINK,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// Reset state for other tests.
|
||||
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
|
||||
await waitForIdle();
|
||||
});
|
||||
|
||||
// This test clicks a link that has apostrophes in the both the path and list
|
||||
// of query parameters, and uses search telemetry with no nonAdsRegexps defined,
|
||||
// which will force us to cache every non ads link in a map and pass it back to
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", "https://example.com/hello_world", false);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", "redirect_final.sjs", false);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", "redirect_twice.sjs", false);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", "redirect_once.sjs", false);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Page will do a redirect</title>
|
||||
<meta content="0;url=https://example.com/hello_world" http-equiv="refresh">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -15,7 +15,6 @@
|
|||
<a href="https://example.com/ad/1">
|
||||
<h2>Example Result</h2>
|
||||
</a>
|
||||
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras ac velit sed tellus facilisis euismod.</p>
|
||||
<span><a href="https://example.com/ad/2">Ad link that says there are 10 Locations nearby</a></span>
|
||||
<div class="multi-col">
|
||||
<div>
|
||||
|
@ -47,10 +46,9 @@
|
|||
</div>
|
||||
<div class="moz_ad">
|
||||
<h5 test-label>ad_link</h5>
|
||||
<a href="https://example.com/ad/7">
|
||||
<a id="ad_link_redirect" href="https://example.org/browser/browser/components/search/test/browser/searchTelemetryAd_adsLink_redirect.html">
|
||||
<h2>Example Shop</h2>
|
||||
</a>
|
||||
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras ac velit sed tellus facilisis euismod.</p>
|
||||
<div class="factrow">
|
||||
<a href="https://example.com/ad/8">Home Page</a>
|
||||
<a href="https://example.com/ad/9">Products</a>
|
||||
|
@ -62,23 +60,23 @@
|
|||
<a href="https://example.com/ad/11">
|
||||
<h2>Example Shop</h2>
|
||||
</a>
|
||||
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras ac velit sed tellus facilisis euismod.</p>
|
||||
<div class="factrow">
|
||||
<a href="https://example.com/ad/12">Home Page</a>
|
||||
<a href="https://example.com/ad/13">Products</a>
|
||||
<a href="https://example.com/ad/14">Sales</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 test-label>non_ads_link</h5>
|
||||
<a id="non_ads_link" href="https://example.com/browser/browser/components/search/test/browser/cacheable.html">
|
||||
<h2>Example of a cached non ad</h2>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5 test-label>non_ads_link</h5>
|
||||
Example of a cached non ad
|
||||
</a><br />
|
||||
<a id="non_ads_link_redirected" href="https://example.org/browser/browser/components/search/test/browser/searchTelemetryAd_nonAdsLink_redirect.html">
|
||||
Example of a redirected non ad link
|
||||
</a><br />
|
||||
<a id="non_ads_link_redirected_no_top_level" href="#">
|
||||
Example of a redirected non ad link that isn't initially top level loaded
|
||||
</a><br />
|
||||
<a id="non_ads_link_multiple_redirects" href="https://example.com/browser/browser/components/search/test/browser/redirect_thrice.sjs">
|
||||
Example of a redirected non ad link that's redirected multiple times
|
||||
</a><br />
|
||||
<a id="non_ads_link_with_special_characters_in_path" href="https://example.com/path'?hello_world&foo=bar's">
|
||||
<h2>Example of a non ad</h2>
|
||||
Example of a non ad with special characters in path
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -96,5 +94,19 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<iframe style="display: none;"></iframe>
|
||||
<script>
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.origin == "https://example.org") {
|
||||
window.location.href = event.data;
|
||||
}
|
||||
});
|
||||
document.getElementById("non_ads_link_redirected_no_top_level")
|
||||
.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
let iframe = document.querySelector("iframe");
|
||||
iframe.src = "https://example.org/browser/browser/components/search/test/browser/searchTelemetryAd_nonAdsLink_redirect_nonTopLoaded.html";
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Page will do a redirect without doing it in a top load</title>
|
||||
<!-- <meta content="0;url=https://example.com/hello_world" http-equiv="refresh"> -->
|
||||
<script>
|
||||
let parentWindow = window.parent;
|
||||
let url = "https://example.com/hello_world";
|
||||
parentWindow.postMessage(url, "*");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
Cache-Control: no-cache, must-revalidate
|
||||
Pragma: no-cache
|
||||
Expires: Fri, 01 Jan 1990 00:00:00 GMT
|
||||
Content-Type: text/html; charset=ISO-8859-1
|
Загрузка…
Ссылка в новой задаче