From ec638a233b162044387c157559d29a1c2544b736 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 21 Jul 2023 02:06:44 -0600 Subject: [PATCH 01/19] fix(mv3): Reverting Telemetry Changes To Use PatchedCountlySDK Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/landing-pages/welcome/store.js | 4 +-- add-on/src/lib/ipfs-companion.js | 18 +++++++++++-- add-on/src/lib/{telemetry.js => telemetry.ts} | 25 +++++++++++++------ add-on/src/options/store.js | 4 +-- add-on/src/popup/browser-action/store.js | 10 ++------ add-on/src/popup/quick-import.js | 4 +-- add-on/src/recovery/recovery.js | 4 +-- 7 files changed, 39 insertions(+), 30 deletions(-) rename add-on/src/lib/{telemetry.js => telemetry.ts} (61%) diff --git a/add-on/src/landing-pages/welcome/store.js b/add-on/src/landing-pages/welcome/store.js index 7d9a31fb2..83f733d10 100644 --- a/add-on/src/landing-pages/welcome/store.js +++ b/add-on/src/landing-pages/welcome/store.js @@ -1,7 +1,6 @@ 'use strict' /* eslint-env browser, webextensions */ import browser from 'webextension-polyfill' -import { handleConsentFromState, trackView } from '../../lib/telemetry.js' export default function createWelcomePageStore (i18n, runtime) { return function welcomePageStore (state, emitter) { @@ -10,8 +9,7 @@ export default function createWelcomePageStore (i18n, runtime) { state.webuiRootUrl = null let port emitter.on('DOMContentLoaded', async () => { - handleConsentFromState(state) - trackView('welcome') + browser.runtime.sendMessage({ telemetry: { trackView: 'welcome' } }) emitter.emit('render') port = runtime.connect({ name: 'browser-action-port' }) port.onMessage.addListener(async (message) => { diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 163b4e157..7652a5f54 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -24,7 +24,7 @@ import { guiURLString, migrateOptions, optionDefaults, safeURL, storeMissingOpti import { getExtraInfoSpec } from './redirect-handler/blockOrObserve.js' import createRuntimeChecks from './runtime-checks.js' import { initState, offlinePeerCount } from './state.js' -import { handleConsentFromState, trackView } from '../lib/telemetry.js' +import { endSession, handleConsentFromState, startSession, trackView } from '../lib/telemetry.js' // this won't work in webworker context. Needs to be enabled manually // https://github.com/debug-js/debug/issues/916 @@ -66,7 +66,8 @@ export default async function init (inQuickImport = false) { if (state.active) { // It's ok for this to fail, node might be unavailable or mis-configured try { - handleConsentFromState(state) + await handleConsentFromState(state) + startSession() ipfs = await initIpfsClient(browser, state, inQuickImport) trackView('init') } catch (err) { @@ -180,6 +181,16 @@ export default async function init (inQuickImport = false) { const result = await validIpfsOrIpns(path) ? await resolveToPublicUrl(path) : null return { pubGwUrlForIpfsOrIpnsPath: result } } + if (request.telemetry) { + return Promise.resolve(onTelemetryMessage(request.telemetry)) + } + } + + function onTelemetryMessage (request) { + if (request.trackView) { + const { version } = browser.runtime.getManifest() + return trackView(request.trackView, { version }) + } } // PORTS (connection-based messaging) @@ -584,6 +595,7 @@ export default async function init (inQuickImport = false) { await registerSubdomainProxy(getState, runtime) shouldRestartIpfsClient = true shouldStopIpfsClient = !state.active + state.active ? startSession() : endSession() break case 'ipfsNodeType': if (change.oldValue !== braveNodeType && change.newValue === braveNodeType) { @@ -650,6 +662,8 @@ export default async function init (inQuickImport = false) { break } } + // ensure consent is set properly on state changes + handleConsentFromState(state) if ((state.active && shouldRestartIpfsClient) || shouldStopIpfsClient) { try { diff --git a/add-on/src/lib/telemetry.js b/add-on/src/lib/telemetry.ts similarity index 61% rename from add-on/src/lib/telemetry.js rename to add-on/src/lib/telemetry.ts index eec136a05..0793f45df 100644 --- a/add-on/src/lib/telemetry.js +++ b/add-on/src/lib/telemetry.ts @@ -3,10 +3,12 @@ import MetricsProvider from '@ipfs-shipyard/ignite-metrics/MetricsProvider' import PatchedCountly from 'countly-sdk-web' import debug from 'debug' import { WebExtensionStorageProvider } from './storage-provider/WebExtensionStorageProvider.js' +import { CompanionState } from '../types/companion.js' +import { consentTypes } from '@ipfs-shipyard/ignite-metrics' const log = debug('ipfs-companion:telemetry') -const metricsProvider = new MetricsProvider({ +const metricsProvider = new MetricsProvider({ appKey: '393f72eb264c28a1b59973da1e0a3938d60dc38a', autoTrack: false, metricsService: PatchedCountly, @@ -19,7 +21,7 @@ const metricsProvider = new MetricsProvider({ * @param {import('../types/companion.js').CompanionState} state * @returns {void} */ -export function handleConsentFromState (state) { +export async function handleConsentFromState (state: CompanionState): Promise { const telemetryGroups = { minimal: state?.telemetryGroupMinimal || false, performance: state?.telemetryGroupPerformance || false, @@ -30,20 +32,27 @@ export function handleConsentFromState (state) { for (const [groupName, isEnabled] of Object.entries(telemetryGroups)) { if (isEnabled) { log(`Adding consent for '${groupName}'`) - metricsProvider.addConsent(groupName) + await metricsProvider.addConsent(groupName as consentTypes) } else { log(`Removing consent for '${groupName}'`) - metricsProvider.removeConsent(groupName) + await metricsProvider.removeConsent(groupName as consentTypes) } } } -const ignoredViewsRegex = [] -export function trackView (view, segments) { +const ignoredViewsRegex: RegExp[] = [] + +/** + * TrackView is a wrapper around ignite-metrics trackView + * + * @param view + * @param segments + */ +export function trackView (view: string, segments: Record = {}): void { log('trackView called for view: ', view) const { version } = browser.runtime.getManifest() metricsProvider.trackView(view, ignoredViewsRegex, { ...segments, version }) } -export const startSession = (...args) => metricsProvider.startSession(...args) -export const endSession = (...args) => metricsProvider.endSession(...args) +export const startSession = (...args: any[]): void => metricsProvider.startSession(...args) +export const endSession = (...args: any[]): void => metricsProvider.endSession(...args) diff --git a/add-on/src/options/store.js b/add-on/src/options/store.js index 80f4405a3..09a3eaf67 100644 --- a/add-on/src/options/store.js +++ b/add-on/src/options/store.js @@ -5,7 +5,6 @@ import browser from 'webextension-polyfill' import { optionDefaults } from '../lib/options.js' import { notifyOptionChange, notifyStateChange } from '../lib/redirect-handler/blockOrObserve.js' import createRuntimeChecks from '../lib/runtime-checks.js' -import { handleConsentFromState, trackView } from '../lib/telemetry.js' // The store contains and mutates the state for the app export default function optionStore (state, emitter) { @@ -22,8 +21,7 @@ export default function optionStore (state, emitter) { } emitter.on('DOMContentLoaded', async () => { - handleConsentFromState(state) - trackView('options') + browser.runtime.sendMessage({ telemetry: { trackView: 'options' } }) updateStateOptions() browser.storage.onChanged.addListener(updateStateOptions) }) diff --git a/add-on/src/popup/browser-action/store.js b/add-on/src/popup/browser-action/store.js index 9cf24f1be..de5580cbe 100644 --- a/add-on/src/popup/browser-action/store.js +++ b/add-on/src/popup/browser-action/store.js @@ -8,7 +8,6 @@ import { contextMenuCopyAddressAtPublicGw, contextMenuCopyCanonicalAddress, cont import { browserActionFilesCpImportCurrentTab } from '../../lib/ipfs-import.js' import { ipfsContentPath } from '../../lib/ipfs-path.js' import { notifyStateChange } from '../../lib/redirect-handler/blockOrObserve.js' -import { endSession, handleConsentFromState, startSession, trackView } from '../../lib/telemetry.js' import { POSSIBLE_NODE_TYPES } from '../../lib/state.js' // The store contains and mutates the state for the app @@ -41,8 +40,7 @@ export default (state, emitter) => { let port emitter.on('DOMContentLoaded', async () => { - handleConsentFromState(state) - trackView('browser-action') + browser.runtime.sendMessage({ telemetry: { trackView: 'browser-action' } }) // initial render with status stub emitter.emit('render') @@ -211,10 +209,7 @@ export default (state, emitter) => { state.active = !prev try { await browser.storage.local.set({ active: state.active }) - if (state.active) { - startSession() - } else { - endSession() + if (!state.active) { state.gatewayAddress = state.pubGwURLString state.ipfsApiUrl = null state.gatewayVersion = null @@ -223,7 +218,6 @@ export default (state, emitter) => { } await notifyStateChange() await browser.storage.local.set({ active: state.active }) - handleConsentFromState(state) } catch (error) { console.error(`Unable to update global Active flag due to ${error}`) state.active = prev diff --git a/add-on/src/popup/quick-import.js b/add-on/src/popup/quick-import.js index 0a3d24419..9895bc5d1 100644 --- a/add-on/src/popup/quick-import.js +++ b/add-on/src/popup/quick-import.js @@ -12,7 +12,6 @@ import browser from 'webextension-polyfill' import * as externalApiClient from '../lib/ipfs-client/external.js' import createIpfsCompanion from '../lib/ipfs-companion.js' import { formatImportDirectory } from '../lib/ipfs-import.js' -import { handleConsentFromState, trackView } from '../lib/telemetry.js' import logo from './logo.js' import { POSSIBLE_NODE_TYPES } from '../lib/state.js' @@ -51,8 +50,7 @@ function quickImportStore (state, emitter) { let port emitter.on('DOMContentLoaded', async () => { - handleConsentFromState(state) - trackView('quick-import') + browser.runtime.sendMessage({ telemetry: { trackView: 'quick-import' } }) // initialize connection to the background script which will trigger UI updates port = browser.runtime.connect({ name: 'browser-action-port' }) port.onMessage.addListener(async (message) => { diff --git a/add-on/src/recovery/recovery.js b/add-on/src/recovery/recovery.js index 822d60fee..1cc3447e3 100644 --- a/add-on/src/recovery/recovery.js +++ b/add-on/src/recovery/recovery.js @@ -7,7 +7,6 @@ import { i18n, runtime } from 'webextension-polyfill' import { nodeOffSvg } from '../landing-pages/welcome/page.js' import createWelcomePageStore from '../landing-pages/welcome/store.js' import { optionsPage } from '../lib/constants.js' -import { handleConsentFromState, trackView } from '../lib/telemetry.js' import './recovery.css' const app = choo() @@ -20,8 +19,7 @@ const optionsPageLink = html` { - handleConsentFromState(state) - trackView('recovery') + browser.runtime.sendMessage({ telemetry: { trackView: 'recovery' } }) const { hash } = window.location const { href: publicURI } = new URL(decodeURIComponent(hash.slice(1))) From cf2ca845558337810186438ff2b1d94c80868556 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 21 Jul 2023 02:12:42 -0600 Subject: [PATCH 02/19] fix(mv3): metrics build Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/telemetry.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/add-on/src/lib/telemetry.ts b/add-on/src/lib/telemetry.ts index 0793f45df..cd1243872 100644 --- a/add-on/src/lib/telemetry.ts +++ b/add-on/src/lib/telemetry.ts @@ -1,5 +1,5 @@ import browser from 'webextension-polyfill' -import MetricsProvider from '@ipfs-shipyard/ignite-metrics/MetricsProvider' +import MetricsProvider from '@ipfs-shipyard/ignite-metrics/browser-vanilla' import PatchedCountly from 'countly-sdk-web' import debug from 'debug' import { WebExtensionStorageProvider } from './storage-provider/WebExtensionStorageProvider.js' @@ -8,7 +8,7 @@ import { consentTypes } from '@ipfs-shipyard/ignite-metrics' const log = debug('ipfs-companion:telemetry') -const metricsProvider = new MetricsProvider({ +const metricsProvider = new MetricsProvider({ appKey: '393f72eb264c28a1b59973da1e0a3938d60dc38a', autoTrack: false, metricsService: PatchedCountly, From f26abf0dcfa77f10a1419fd0a7ef6bfc97ca0dd3 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 21 Jul 2023 02:41:09 -0600 Subject: [PATCH 03/19] fix: more reverts + fixing patch Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/telemetry.ts | 5 +- patches/countly-sdk-web+23.2.2.patch | 139 +++++++++++++++++---------- 2 files changed, 91 insertions(+), 53 deletions(-) diff --git a/add-on/src/lib/telemetry.ts b/add-on/src/lib/telemetry.ts index cd1243872..941ae8a32 100644 --- a/add-on/src/lib/telemetry.ts +++ b/add-on/src/lib/telemetry.ts @@ -48,10 +48,9 @@ const ignoredViewsRegex: RegExp[] = [] * @param view * @param segments */ -export function trackView (view: string, segments: Record = {}): void { +export function trackView (view: string, segments: Record): void { log('trackView called for view: ', view) - const { version } = browser.runtime.getManifest() - metricsProvider.trackView(view, ignoredViewsRegex, { ...segments, version }) + metricsProvider.trackView(view, ignoredViewsRegex, segments) } export const startSession = (...args: any[]): void => metricsProvider.startSession(...args) diff --git a/patches/countly-sdk-web+23.2.2.patch b/patches/countly-sdk-web+23.2.2.patch index 79e4121a7..24f0094de 100644 --- a/patches/countly-sdk-web+23.2.2.patch +++ b/patches/countly-sdk-web+23.2.2.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/countly-sdk-web/lib/countly.js b/node_modules/countly-sdk-web/lib/countly.js -index da26eb6..620cb3e 100644 +index da26eb6..f89a335 100644 --- a/node_modules/countly-sdk-web/lib/countly.js +++ b/node_modules/countly-sdk-web/lib/countly.js @@ -52,10 +52,22 @@ @@ -149,16 +149,23 @@ index da26eb6..620cb3e 100644 } if (useSessionCookie) { -@@ -2071,7 +2083,7 @@ - else if (typeof document.referrer !== "undefined" && document.referrer.length) { - var matches = urlParseRE.exec(document.referrer); - // do not report referrers of current website -- if (matches && matches[11] && matches[11] !== window.location.hostname) { -+ if (matches && matches[11] && matches[11] !== globalThis.location.hostname) { +@@ -2066,15 +2078,6 @@ segments.start = 1; } } -@@ -2147,7 +2159,7 @@ +- // if we are not using session cookie, there is no session state between refreshes +- // so we fallback to old logic of landing +- else if (typeof document.referrer !== "undefined" && document.referrer.length) { +- var matches = urlParseRE.exec(document.referrer); +- // do not report referrers of current website +- if (matches && matches[11] && matches[11] !== window.location.hostname) { +- segments.start = 1; +- } +- } + + if (viewSegments) { + viewSegments = truncateObject(viewSegments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "track_pageview", log); +@@ -2147,7 +2150,7 @@ // truncate new segment segments = truncateObject(segments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "processClick", log); if (self.track_domains) { @@ -167,7 +174,7 @@ index da26eb6..620cb3e 100644 } add_cly_events({ key: internalEventKeyEnums.ACTION, -@@ -2173,7 +2185,7 @@ +@@ -2173,7 +2176,7 @@ if (parent) { log(logLevelEnums.INFO, "track_scrolls, Tracking the specified children"); } @@ -176,7 +183,7 @@ index da26eb6..620cb3e 100644 isScrollRegistryOpen = true; trackingScrolls = true; -@@ -2813,8 +2825,8 @@ +@@ -2813,8 +2816,8 @@ else { var pages = widgets[i].target_pages; for (var k = 0; k < pages.length; k++) { @@ -187,7 +194,7 @@ index da26eb6..620cb3e 100644 var isContainAsterisk = pages[k].includes("*"); if (((isContainAsterisk && isWildcardMatched) || isFullPathMatched) && !widgets[i].hide_sticker) { processWidget(widgets[i], true); -@@ -3048,7 +3060,7 @@ +@@ -3048,7 +3051,7 @@ return; } @@ -196,7 +203,7 @@ index da26eb6..620cb3e 100644 var feedbackWidgetFamily; // set feedback widget family as ratings and load related style file when type is ratings -@@ -3170,7 +3182,7 @@ +@@ -3170,7 +3173,7 @@ wrapper.appendChild(iframe); log(logLevelEnums.DEBUG, "present_feedback_widget, Appended the iframe"); @@ -205,7 +212,7 @@ index da26eb6..620cb3e 100644 var data = {}; try { data = JSON.parse(e.data); -@@ -3249,9 +3261,9 @@ +@@ -3249,9 +3252,9 @@ break; case "onScrollHalfwayDown": @@ -217,26 +224,34 @@ index da26eb6..620cb3e 100644 var documentHeight = getDocHeight(); if (scrollY >= (documentHeight / 2)) { surveyShown = true; -@@ -3841,13 +3853,13 @@ - var height = (screen.height) ? parseInt(screen.height) : 0; - if (width !== 0 && height !== 0) { - var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); +@@ -3835,33 +3838,11 @@ + metrics._app_version = metrics._app_version || self.app_version; + metrics._ua = metrics._ua || currentUserAgentString(); + +- // getting resolution +- if (screen.width) { +- var width = (screen.width) ? parseInt(screen.width) : 0; +- var height = (screen.height) ? parseInt(screen.height) : 0; +- if (width !== 0 && height !== 0) { +- var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); - if (iOS && window.devicePixelRatio) { -+ if (iOS && globalThis.devicePixelRatio) { - // ios provides dips, need to multiply +- // ios provides dips, need to multiply - width = Math.round(width * window.devicePixelRatio); - height = Math.round(height * window.devicePixelRatio); -+ width = Math.round(width * globalThis.devicePixelRatio); -+ height = Math.round(height * globalThis.devicePixelRatio); - } - else { +- } +- else { - if (Math.abs(window.orientation) === 90) { -+ if (Math.abs(globalThis.orientation) === 90) { - // we have landscape orientation - // switch values for all except ios - var temp = width; -@@ -3860,8 +3872,8 @@ - } +- // we have landscape orientation +- // switch values for all except ios +- var temp = width; +- width = height; +- height = temp; +- } +- } +- metrics._resolution = metrics._resolution || "" + width + "x" + height; +- } +- } ++ metrics._resolution = metrics._resolution || '0x0'; // getting density ratio - if (window.devicePixelRatio) { @@ -246,16 +261,40 @@ index da26eb6..620cb3e 100644 } // getting locale -@@ -3873,7 +3885,7 @@ - if (typeof document.referrer !== "undefined" && document.referrer.length) { - var matches = urlParseRE.exec(document.referrer); - // do not report referrers of current website +@@ -3870,32 +3851,6 @@ + metrics._locale = metrics._locale || locale; + } + +- if (typeof document.referrer !== "undefined" && document.referrer.length) { +- var matches = urlParseRE.exec(document.referrer); +- // do not report referrers of current website - if (matches && matches[11] && matches[11] !== window.location.hostname) { -+ if (matches && matches[11] && matches[11] !== globalThis.location.hostname) { - var ignoring = false; - if (ignoreReferrers && ignoreReferrers.length) { - for (var k = 0; k < ignoreReferrers.length; k++) { -@@ -3963,72 +3975,62 @@ +- var ignoring = false; +- if (ignoreReferrers && ignoreReferrers.length) { +- for (var k = 0; k < ignoreReferrers.length; k++) { +- try { +- var reg = new RegExp(ignoreReferrers[k]); +- if (reg.test(document.referrer)) { +- log(logLevelEnums.DEBUG, "Ignored: " + document.referrer); +- ignoring = true; +- break; +- } +- } +- catch (ex) { +- log(logLevelEnums.ERROR, "Problem with ignoring: " + ignoreReferrers[k], ", error: " + ex); +- } +- } +- } +- if (!ignoring) { +- metrics._store = metrics._store || document.referrer; +- } +- } +- } +- + log(logLevelEnums.DEBUG, "Got metrics", metrics); + return metrics; + } +@@ -3963,72 +3918,62 @@ */ function sendXmlHttpRequest(functionName, url, params, callback, useBroadResponseValidator) { useBroadResponseValidator = useBroadResponseValidator || false; @@ -355,12 +394,12 @@ index da26eb6..620cb3e 100644 - }; - if (method === "GET") { - xhr.send(); +- } +- else { +- xhr.send(data); + } else { + throw new Error(response.statusText); } -- else { -- xhr.send(data); -- } - } - catch (e) { + }).catch(function (e) { @@ -375,7 +414,7 @@ index da26eb6..620cb3e 100644 } /** -@@ -4105,7 +4107,7 @@ +@@ -4105,7 +4050,7 @@ * */ function processScroll() { @@ -384,7 +423,7 @@ index da26eb6..620cb3e 100644 } /** -@@ -4131,7 +4133,7 @@ +@@ -4131,7 +4076,7 @@ // truncate new segment segments = truncateObject(segments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "processScrollView", log); if (self.track_domains) { @@ -393,7 +432,7 @@ index da26eb6..620cb3e 100644 } add_cly_events({ key: internalEventKeyEnums.ACTION, -@@ -4862,7 +4864,7 @@ +@@ -4862,7 +4807,7 @@ */ var get_event_target = function(event) { if (!event) { @@ -402,7 +441,7 @@ index da26eb6..620cb3e 100644 } if (typeof event.target !== "undefined") { return event.target; -@@ -4924,7 +4926,7 @@ +@@ -4924,7 +4869,7 @@ var device = "desktop"; // regexps corresponding to tablets or phones that can be found in userAgent string @@ -411,7 +450,7 @@ index da26eb6..620cb3e 100644 var phoneCheck = /(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|nintendo ds|archos|skyfire|puffin|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard)/; // check whether the regexp values corresponds to something in the user agent string -@@ -5009,7 +5011,7 @@ +@@ -5009,7 +4954,7 @@ return Math.min( Math.min(D.body.clientHeight, D.documentElement.clientHeight), Math.min(D.body.offsetHeight, D.documentElement.offsetHeight), @@ -420,7 +459,7 @@ index da26eb6..620cb3e 100644 ); } -@@ -5018,13 +5020,13 @@ +@@ -5018,13 +4963,13 @@ * @returns {String} device orientation */ function getOrientation() { @@ -436,7 +475,7 @@ index da26eb6..620cb3e 100644 var parts = (e.key + "").split("/"); var key = parts.pop(); var appKey = parts.pop(); -@@ -5171,7 +5173,7 @@ +@@ -5171,7 +5116,7 @@ * @return {string} view name * */ Countly.getViewName = function() { @@ -445,7 +484,7 @@ index da26eb6..620cb3e 100644 }; /** -@@ -5179,7 +5181,7 @@ +@@ -5179,7 +5124,7 @@ * @return {string} view url * */ Countly.getViewUrl = function() { @@ -454,7 +493,7 @@ index da26eb6..620cb3e 100644 }; /** -@@ -5187,7 +5189,7 @@ +@@ -5187,7 +5132,7 @@ * @return {string} view url * */ Countly.getSearchQuery = function() { From 63bccaeabc25b21a8d4cbe23f9c9228d45dc0023 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Mon, 24 Jul 2023 00:41:40 -0600 Subject: [PATCH 04/19] feat: adding request view. Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/ipfs-companion.js | 9 +++++++++ add-on/src/lib/ipfs-request.js | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 7652a5f54..3f99bea7f 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -184,6 +184,15 @@ export default async function init (inQuickImport = false) { if (request.telemetry) { return Promise.resolve(onTelemetryMessage(request.telemetry)) } + if (request.request) { + return Promise.resolve(onTrackRequest(request.request)) + } + } + + function onTrackRequest(request) { + if (request && request.url) { + return trackView(request.url, { version: browser.runtime.getManifest().version, type: request.type }) + } } function onTelemetryMessage (request) { diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index 480870ffa..a89f75c33 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -11,6 +11,7 @@ import { safeURL } from './options.js' import { braveNodeType } from './ipfs-client/brave.js' import { recoveryPagePath } from './constants.js' import { addRuleToDynamicRuleSetGenerator, isLocalHost, supportsBlock } from './redirect-handler/blockOrObserve.js' +import { browser } from 'webextension-polyfill' const log = debug('ipfs-companion:request') log.error = debug('ipfs-companion:request:error') @@ -144,6 +145,10 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida async onBeforeRequest (request) { const state = getState() if (!state.active) return + browser.runtime.sendMessage({ type: 'ipfs-companion:request', request: { + url: request.url, + type: request.type, + }}) // When local IPFS node is unreachable , show recovery page where user can redirect // to public gateway. @@ -481,9 +486,21 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida function handleRedirection ({ originUrl, redirectUrl }) { if (redirectUrl !== '' && originUrl !== '' && redirectUrl !== originUrl) { if (supportsBlock) { + browser.runtime.sendMessage({ + type: 'ipfs-companion:request', request: { + url: originUrl, + type: 'main_frame' + } + }) return { redirectUrl } } + browser.runtime.sendMessage({ + type: 'ipfs-companion:request', request: { + url: originUrl, + type: 'main_frame' + } + }) // Let browser handle redirection MV3 style. addRuleToDynamicRuleSet({ originUrl, redirectUrl }) } From e39a4eb7ef11677e835b589d525e7568783114af Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Tue, 25 Jul 2023 12:55:52 -0600 Subject: [PATCH 05/19] Reverting to mainline rc patch --- patches/countly-sdk-web+23.2.2.patch | 162 ++++++++++++--------------- 1 file changed, 72 insertions(+), 90 deletions(-) diff --git a/patches/countly-sdk-web+23.2.2.patch b/patches/countly-sdk-web+23.2.2.patch index 3a711a2bf..a3f99907e 100644 --- a/patches/countly-sdk-web+23.2.2.patch +++ b/patches/countly-sdk-web+23.2.2.patch @@ -28,9 +28,9 @@ index da26eb6..53c31d0 100644 // console.error("Not running in browser"); return; @@ -347,12 +359,12 @@ - + checkIgnore(); - + - if (window.name && window.name.indexOf("cly:") === 0) { + if (globalThis.name && globalThis.name.indexOf("cly:") === 0) { try { @@ -78,7 +78,7 @@ index da26eb6..53c31d0 100644 error += msg + "\n"; @@ -1666,7 +1678,7 @@ }; - + // error handling for 'uncaught rejections' - window.addEventListener("unhandledrejection", function(event) { + globalThis.addEventListener("unhandledrejection", function(event) { @@ -95,27 +95,27 @@ index da26eb6..53c31d0 100644 sendEventsForced(); self.end_session(); }); - + - // manage sessions on window visibility events + // manage sessions on globalThis visibility events var hidden = "hidden"; - + /** @@ -1931,17 +1943,17 @@ } - + // add focus handling eventListeners - add_event(window, "focus", onchange); - add_event(window, "blur", onchange); + add_event(globalThis, "focus", onchange); + add_event(globalThis, "blur", onchange); - + // newer mobile compatible way - add_event(window, "pageshow", onchange); - add_event(window, "pagehide", onchange); + add_event(globalThis, "pageshow", onchange); + add_event(globalThis, "pagehide", onchange); - + // IE 9 and lower: if ("onfocusin" in document) { - add_event(window, "focusin", onchange); @@ -123,12 +123,12 @@ index da26eb6..53c31d0 100644 + add_event(globalThis, "focusin", onchange); + add_event(globalThis, "focusout", onchange); } - + // Page Visibility API for changing tabs and minimizing browser @@ -1971,10 +1983,10 @@ inactivityCounter = 0; } - + - add_event(window, "mousemove", resetInactivity); - add_event(window, "click", resetInactivity); - add_event(window, "keydown", resetInactivity); @@ -137,7 +137,7 @@ index da26eb6..53c31d0 100644 + add_event(globalThis, "click", resetInactivity); + add_event(globalThis, "keydown", resetInactivity); + add_event(globalThis, "scroll", resetInactivity); - + // track user inactivity setInterval(function() { @@ -2048,7 +2060,7 @@ @@ -147,25 +147,18 @@ index da26eb6..53c31d0 100644 - segments.domain = window.location.hostname; + segments.domain = globalThis.location.hostname; } - + if (useSessionCookie) { -@@ -2066,15 +2078,6 @@ +@@ -2071,7 +2083,7 @@ + else if (typeof document.referrer !== "undefined" && document.referrer.length) { + var matches = urlParseRE.exec(document.referrer); + // do not report referrers of current website +- if (matches && matches[11] && matches[11] !== window.location.hostname) { ++ if (matches && matches[11] && matches[11] !== globalThis.location.hostname) { segments.start = 1; } } -- // if we are not using session cookie, there is no session state between refreshes -- // so we fallback to old logic of landing -- else if (typeof document.referrer !== "undefined" && document.referrer.length) { -- var matches = urlParseRE.exec(document.referrer); -- // do not report referrers of current website -- if (matches && matches[11] && matches[11] !== window.location.hostname) { -- segments.start = 1; -- } -- } - - if (viewSegments) { - viewSegments = truncateObject(viewSegments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "track_pageview", log); -@@ -2147,7 +2150,7 @@ +@@ -2147,7 +2159,7 @@ // truncate new segment segments = truncateObject(segments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "processClick", log); if (self.track_domains) { @@ -174,7 +167,7 @@ index da26eb6..53c31d0 100644 } add_cly_events({ key: internalEventKeyEnums.ACTION, -@@ -2173,7 +2176,7 @@ +@@ -2173,7 +2185,7 @@ if (parent) { log(logLevelEnums.INFO, "track_scrolls, Tracking the specified children"); } @@ -182,8 +175,8 @@ index da26eb6..53c31d0 100644 + parent = parent || globalThis; isScrollRegistryOpen = true; trackingScrolls = true; - -@@ -2813,8 +2816,8 @@ + +@@ -2813,8 +2825,8 @@ else { var pages = widgets[i].target_pages; for (var k = 0; k < pages.length; k++) { @@ -194,27 +187,27 @@ index da26eb6..53c31d0 100644 var isContainAsterisk = pages[k].includes("*"); if (((isContainAsterisk && isWildcardMatched) || isFullPathMatched) && !widgets[i].hide_sticker) { processWidget(widgets[i], true); -@@ -3048,7 +3051,7 @@ +@@ -3048,7 +3060,7 @@ return; } - + - var passedOrigin = window.origin || window.location.origin; + var passedOrigin = globalThis.origin || globalThis.location.origin; var feedbackWidgetFamily; - + // set feedback widget family as ratings and load related style file when type is ratings -@@ -3170,7 +3173,7 @@ +@@ -3170,7 +3182,7 @@ wrapper.appendChild(iframe); log(logLevelEnums.DEBUG, "present_feedback_widget, Appended the iframe"); - + - add_event(window, "message", function(e) { + add_event(globalThis, "message", function(e) { var data = {}; try { data = JSON.parse(e.data); -@@ -3249,9 +3252,9 @@ +@@ -3249,9 +3261,9 @@ break; - + case "onScrollHalfwayDown": - add_event(window, "scroll", function() { + add_event(globalThis, "scroll", function() { @@ -224,50 +217,39 @@ index da26eb6..53c31d0 100644 var documentHeight = getDocHeight(); if (scrollY >= (documentHeight / 2)) { surveyShown = true; -@@ -3835,33 +3838,11 @@ - metrics._app_version = metrics._app_version || self.app_version; - metrics._ua = metrics._ua || currentUserAgentString(); - -- // getting resolution -- if (screen.width) { -- var width = (screen.width) ? parseInt(screen.width) : 0; -- var height = (screen.height) ? parseInt(screen.height) : 0; -- if (width !== 0 && height !== 0) { -- var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); +@@ -3841,13 +3853,13 @@ + var height = (screen.height) ? parseInt(screen.height) : 0; + if (width !== 0 && height !== 0) { + var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); - if (iOS && window.devicePixelRatio) { -- // ios provides dips, need to multiply ++ if (iOS && globalThis.devicePixelRatio) { + // ios provides dips, need to multiply - width = Math.round(width * window.devicePixelRatio); - height = Math.round(height * window.devicePixelRatio); -- } -- else { ++ width = Math.round(width * globalThis.devicePixelRatio); ++ height = Math.round(height * globalThis.devicePixelRatio); + } + else { - if (Math.abs(window.orientation) === 90) { -- // we have landscape orientation -- // switch values for all except ios -- var temp = width; -- width = height; -- height = temp; -- } -- } -- metrics._resolution = metrics._resolution || "" + width + "x" + height; -- } -- } -+ metrics._resolution = metrics._resolution || '0x0'; - ++ if (Math.abs(globalThis.orientation) === 90) { + // we have landscape orientation + // switch values for all except ios + var temp = width; +@@ -3860,8 +3872,8 @@ + } + // getting density ratio - if (window.devicePixelRatio) { - metrics._density = metrics._density || window.devicePixelRatio; + if (globalThis.devicePixelRatio) { + metrics._density = metrics._density || globalThis.devicePixelRatio; } - + // getting locale -@@ -3870,32 +3851,6 @@ - metrics._locale = metrics._locale || locale; - } - -- if (typeof document.referrer !== "undefined" && document.referrer.length) { -- var matches = urlParseRE.exec(document.referrer); -- // do not report referrers of current website +@@ -3873,7 +3885,7 @@ + if (typeof document.referrer !== "undefined" && document.referrer.length) { + var matches = urlParseRE.exec(document.referrer); + // do not report referrers of current website - if (matches && matches[11] && matches[11] !== window.location.hostname) { + if (matches && matches[11] && matches[11] !== globalThis.location.hostname) { var ignoring = false; @@ -382,12 +364,12 @@ index da26eb6..53c31d0 100644 - }; - if (method === "GET") { - xhr.send(); -- } -- else { -- xhr.send(data); + } else { + throw new Error(response.statusText); } +- else { +- xhr.send(data); +- } - } - catch (e) { + }).catch(function (e) { @@ -400,18 +382,18 @@ index da26eb6..53c31d0 100644 - } + }); } - + /** -@@ -4105,7 +4050,7 @@ +@@ -4105,7 +4107,7 @@ * */ function processScroll() { - scrollRegistryTopPosition = Math.max(scrollRegistryTopPosition, window.scrollY, document.body.scrollTop, document.documentElement.scrollTop); + scrollRegistryTopPosition = Math.max(scrollRegistryTopPosition, globalThis.scrollY, document.body.scrollTop, document.documentElement.scrollTop); } - + /** -@@ -4131,7 +4076,7 @@ +@@ -4131,7 +4133,7 @@ // truncate new segment segments = truncateObject(segments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "processScrollView", log); if (self.track_domains) { @@ -420,7 +402,7 @@ index da26eb6..53c31d0 100644 } add_cly_events({ key: internalEventKeyEnums.ACTION, -@@ -4862,7 +4807,7 @@ +@@ -4862,7 +4864,7 @@ */ var get_event_target = function(event) { if (!event) { @@ -429,16 +411,16 @@ index da26eb6..53c31d0 100644 } if (typeof event.target !== "undefined") { return event.target; -@@ -4924,7 +4869,7 @@ +@@ -4924,7 +4926,7 @@ var device = "desktop"; - + // regexps corresponding to tablets or phones that can be found in userAgent string - var tabletCheck = /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/; + var tabletCheck = /(ipad|tablet|(android(?!.*mobile))|(globalThiss(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/; var phoneCheck = /(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|nintendo ds|archos|skyfire|puffin|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard)/; - + // check whether the regexp values corresponds to something in the user agent string -@@ -5009,7 +4954,7 @@ +@@ -5009,7 +5011,7 @@ return Math.min( Math.min(D.body.clientHeight, D.documentElement.clientHeight), Math.min(D.body.offsetHeight, D.documentElement.offsetHeight), @@ -446,15 +428,15 @@ index da26eb6..53c31d0 100644 + globalThis.innerHeight ); } - -@@ -5018,13 +4963,13 @@ + +@@ -5018,13 +5020,13 @@ * @returns {String} device orientation */ function getOrientation() { - return window.innerWidth > window.innerHeight ? "landscape" : "portrait"; + return globalThis.innerWidth > globalThis.innerHeight ? "landscape" : "portrait"; } - + /** * Monitor parallel storage changes like other opened tabs */ @@ -463,30 +445,30 @@ index da26eb6..53c31d0 100644 var parts = (e.key + "").split("/"); var key = parts.pop(); var appKey = parts.pop(); -@@ -5171,7 +5116,7 @@ +@@ -5171,7 +5173,7 @@ * @return {string} view name * */ Countly.getViewName = function() { - return window.location.pathname; + return globalThis.location.pathname; }; - + /** -@@ -5179,7 +5124,7 @@ +@@ -5179,7 +5181,7 @@ * @return {string} view url * */ Countly.getViewUrl = function() { - return window.location.pathname; + return globalThis.location.pathname; }; - + /** -@@ -5187,7 +5132,7 @@ +@@ -5187,7 +5189,7 @@ * @return {string} view url * */ Countly.getSearchQuery = function() { - return window.location.search; + return globalThis.location.search; }; - + /** From dff1d6d67edc245632d962159249dfd113580dc6 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Tue, 25 Jul 2023 12:57:42 -0600 Subject: [PATCH 06/19] Reverting to mainline rc add-on/src/lib/ipfs-companion.js --- add-on/src/lib/ipfs-companion.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 47b55de7f..1f69b3b9e 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -24,7 +24,7 @@ import { guiURLString, migrateOptions, optionDefaults, safeURL, storeMissingOpti import { getExtraInfoSpec } from './redirect-handler/blockOrObserve.js' import createRuntimeChecks from './runtime-checks.js' import { initState, offlinePeerCount } from './state.js' -import { endSession, handleConsentFromState, startSession, trackView } from '../lib/telemetry.js' +import { handleConsentFromState, trackView } from '../lib/telemetry.js' // this won't work in webworker context. Needs to be enabled manually // https://github.com/debug-js/debug/issues/916 @@ -594,7 +594,6 @@ export default async function init (inQuickImport = false) { await registerSubdomainProxy(getState, runtime) shouldRestartIpfsClient = true shouldStopIpfsClient = !state.active - state.active ? startSession() : endSession() break case 'ipfsNodeType': if (change.oldValue !== braveNodeType && change.newValue === braveNodeType) { From 77941006d44058f3789fa41f7b6e0028b29ded7a Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Wed, 26 Jul 2023 03:49:29 -0600 Subject: [PATCH 07/19] feat(telemetry): Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/ipfs-companion.js | 44 ++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 1f69b3b9e..01fbbcaa2 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -25,6 +25,7 @@ import { getExtraInfoSpec } from './redirect-handler/blockOrObserve.js' import createRuntimeChecks from './runtime-checks.js' import { initState, offlinePeerCount } from './state.js' import { handleConsentFromState, trackView } from '../lib/telemetry.js' +import RequestTracker from './message-handler/requestTracker.js' // this won't work in webworker context. Needs to be enabled manually // https://github.com/debug-js/debug/issues/916 @@ -172,19 +173,6 @@ export default async function init (inQuickImport = false) { // =================================================================== // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/sendMessage - async function onRuntimeMessage (request, sender) { - // console.log((sender.tab ? 'Message from a content script:' + sender.tab.url : 'Message from the extension'), request) - if (request.pubGwUrlForIpfsOrIpnsPath) { - const path = request.pubGwUrlForIpfsOrIpnsPath - const { validIpfsOrIpns, resolveToPublicUrl } = ipfsPathValidator - const result = await validIpfsOrIpns(path) ? await resolveToPublicUrl(path) : null - return { pubGwUrlForIpfsOrIpnsPath: result } - } - if (request.telemetry) { - return Promise.resolve(onTelemetryMessage(request.telemetry)) - } - } - function onTelemetryMessage (request) { if (request.trackView) { const { version } = browser.runtime.getManifest() @@ -192,6 +180,36 @@ export default async function init (inQuickImport = false) { } } + /** + * @type {Array<{check: (request: any) => boolean, handler: (request: any, sender?: any) => Promise}>} + */ + const runtimeMessageHandlers = [ + { + check: (request) => request.pubGwUrlForIpfsOrIpnsPath, + handler: async (request) => { + const path = request.pubGwUrlForIpfsOrIpnsPath + const { validIpfsOrIpns, resolveToPublicUrl } = ipfsPathValidator + const result = await validIpfsOrIpns(path) ? await resolveToPublicUrl(path) : null + return { pubGwUrlForIpfsOrIpnsPath: result } + } + }, + { + check: (request) => request.telemetry, + handler: (request) => Promise.resolve(onTelemetryMessage(request.telemetry)) + }, + new RequestTracker('url-observed'), + new RequestTracker('url-resolved') + ] + + async function onRuntimeMessage (request, sender) { + // console.log((sender.tab ? 'Message from a content script:' + sender.tab.url : 'Message from the extension'), request) + try { + return await runtimeMessageHandlers.find(({ check }) => check(request))?.handler(request, sender) + } catch (error) { + log.error('onRuntimeMessage failed', error) + } + } + // PORTS (connection-based messaging) // =================================================================== // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/connect From bbde71638c402972d80dc2b7fbd3fec21b2ac497 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Wed, 26 Jul 2023 03:50:32 -0600 Subject: [PATCH 08/19] feat(telemetry): Implementing RequestTracker Event Handler Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- .../lib/message-handler/IMessageHandler.ts | 6 ++ .../src/lib/message-handler/requestTracker.ts | 55 +++++++++++++++++++ add-on/src/lib/telemetry.ts | 12 ++++ add-on/src/types/countly.d.ts | 9 +++ 4 files changed, 82 insertions(+) create mode 100644 add-on/src/lib/message-handler/IMessageHandler.ts create mode 100644 add-on/src/lib/message-handler/requestTracker.ts create mode 100644 add-on/src/types/countly.d.ts diff --git a/add-on/src/lib/message-handler/IMessageHandler.ts b/add-on/src/lib/message-handler/IMessageHandler.ts new file mode 100644 index 000000000..1ce066322 --- /dev/null +++ b/add-on/src/lib/message-handler/IMessageHandler.ts @@ -0,0 +1,6 @@ +import browser from 'webextension-polyfill' + +export interface IMessageHandler { + check: (message: T) => boolean + handler: (message: T, sender?: browser.Runtime.MessageSender) => Promise +} diff --git a/add-on/src/lib/message-handler/requestTracker.ts b/add-on/src/lib/message-handler/requestTracker.ts new file mode 100644 index 000000000..3d0fb2308 --- /dev/null +++ b/add-on/src/lib/message-handler/requestTracker.ts @@ -0,0 +1,55 @@ +import { trackEvent } from '../telemetry.js' +import { IMessageHandler } from './IMessageHandler.js' + +interface IRequestTrackerMsg { + type: string + requestType: string +} + +export default class RequestTracker implements IMessageHandler { + private readonly eventKey: 'url-observed' | 'url-resolved' + private readonly flushInterval: number = 1000 * 60 * 5 // 5 minutes + private readonly msgKey: string = 'ipfs-companion:track-request:' + private lastSync: number = Date.now() + private requestTypeStore: Record = {} + + constructor (eventKey: 'url-observed' | 'url-resolved') { + this.eventKey = eventKey + this.setupFlushScheduler() + } + + get checkKey (): string { + return this.msgKey + this.eventKey + } + + check ({ type }: IRequestTrackerMsg): boolean { + return type === this.checkKey + } + + async handler ({ requestType }: IRequestTrackerMsg): Promise { + if (!(requestType in this.requestTypeStore)) { + this.requestTypeStore[requestType] = 0 + } + this.requestTypeStore[requestType] += 1 + } + + private flushStore (): void { + const count = Object.values(this.requestTypeStore).reduce((a, b): number => a + b, 0) + trackEvent({ + key: this.eventKey, + count, + dur: Date.now() - this.lastSync, + segmentation: Object.assign({}, this.requestTypeStore) as unknown as Record + }) + // reset + this.lastSync = Date.now() + this.requestTypeStore = {} + } + + private setupFlushScheduler (): void { + setTimeout(() => { + this.flushStore() + this.setupFlushScheduler() + }, this.flushInterval) + } +} diff --git a/add-on/src/lib/telemetry.ts b/add-on/src/lib/telemetry.ts index a55c69eee..6b3eca5f0 100644 --- a/add-on/src/lib/telemetry.ts +++ b/add-on/src/lib/telemetry.ts @@ -4,6 +4,7 @@ import debug from 'debug' import { WebExtensionStorageProvider } from './storage-provider/WebExtensionStorageProvider.js' import { CompanionState } from '../types/companion.js' import { consentTypes } from '@ipfs-shipyard/ignite-metrics' +import type { CountlyEvent } from 'countly-web-sdk' const log = debug('ipfs-companion:telemetry') @@ -51,3 +52,14 @@ export function trackView (view: string, segments: Record): void log('trackView called for view: ', view) metricsProvider.trackView(view, ignoredViewsRegex, segments) } + +/** + * TrackView is a wrapper around ignite-metrics trackView + * + * @param event + * @param segments + */ +export function trackEvent (event: CountlyEvent): void { + log('trackEvent called for event: ', event) + metricsProvider.trackEvent(event) +} diff --git a/add-on/src/types/countly.d.ts b/add-on/src/types/countly.d.ts new file mode 100644 index 000000000..30eedfa6f --- /dev/null +++ b/add-on/src/types/countly.d.ts @@ -0,0 +1,9 @@ +declare module 'countly-web-sdk' { + export interface CountlyEvent { + key: string + count?: number + sum?: number + dur?: number + segmentation?: Record + } +} From 76669252ca1c8719cb01db96d40f765b255fe33d Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Wed, 26 Jul 2023 03:51:06 -0600 Subject: [PATCH 09/19] feat(telemetry): hooking up events. Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/ipfs-request.js | 55 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index a89f75c33..9f8a77806 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -3,15 +3,15 @@ import debug from 'debug' -import LRU from 'lru-cache' -import isIPFS from 'is-ipfs' import isFQDN from 'is-fqdn' +import isIPFS from 'is-ipfs' +import LRU from 'lru-cache' +import browser from 'webextension-polyfill' +import { recoveryPagePath } from './constants.js' +import { braveNodeType } from './ipfs-client/brave.js' import { dropSlash, ipfsUri, pathAtHttpGateway, sameGateway } from './ipfs-path.js' import { safeURL } from './options.js' -import { braveNodeType } from './ipfs-client/brave.js' -import { recoveryPagePath } from './constants.js' import { addRuleToDynamicRuleSetGenerator, isLocalHost, supportsBlock } from './redirect-handler/blockOrObserve.js' -import { browser } from 'webextension-polyfill' const log = debug('ipfs-companion:request') log.error = debug('ipfs-companion:request:error') @@ -145,10 +145,10 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida async onBeforeRequest (request) { const state = getState() if (!state.active) return - browser.runtime.sendMessage({ type: 'ipfs-companion:request', request: { - url: request.url, - type: request.type, - }}) + browser.runtime.sendMessage({ + type: 'ipfs-companion:track-request:url-observed', + requestType: request.type + }) // When local IPFS node is unreachable , show recovery page where user can redirect // to public gateway. @@ -156,7 +156,8 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida const publicUri = await ipfsPathValidator.resolveToPublicUrl(request.url, state.pubGwURLString) return handleRedirection({ originUrl: request.url, - redirectUrl: `${dropSlash(runtimeRoot)}${recoveryPagePath}#${encodeURIComponent(publicUri)}` + redirectUrl: `${dropSlash(runtimeRoot)}${recoveryPagePath}#${encodeURIComponent(publicUri)}`, + type: request.type }) } @@ -167,7 +168,8 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida const redirectUrl = safeURL(request.url, { useLocalhostName: state.useSubdomains }).toString() return handleRedirection({ originUrl: request.url, - redirectUrl + redirectUrl, + type: request.type }) } @@ -176,7 +178,8 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida const redirectUrl = safeURL(request.url, { useLocalhostName: false }).toString() return handleRedirection({ originUrl: request.url, - redirectUrl + redirectUrl, + type: request.type }) } @@ -483,24 +486,16 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida * @param {object} input contains originUrl and redirectUrl. * @returns */ -function handleRedirection ({ originUrl, redirectUrl }) { +function handleRedirection ({ originUrl, redirectUrl, type }) { if (redirectUrl !== '' && originUrl !== '' && redirectUrl !== originUrl) { + browser.runtime.sendMessage({ + type: 'ipfs-companion:track-request:url-resolved', + requestType: type + }) if (supportsBlock) { - browser.runtime.sendMessage({ - type: 'ipfs-companion:request', request: { - url: originUrl, - type: 'main_frame' - } - }) return { redirectUrl } } - browser.runtime.sendMessage({ - type: 'ipfs-companion:request', request: { - url: originUrl, - type: 'main_frame' - } - }) // Let browser handle redirection MV3 style. addRuleToDynamicRuleSet({ originUrl, redirectUrl }) } @@ -555,7 +550,8 @@ async function redirectToGateway (request, url, state, ipfsPathValidator, runtim return handleRedirection({ originUrl: request.url, - redirectUrl + redirectUrl, + type: request.type }) } @@ -625,7 +621,8 @@ function normalizedRedirectingProtocolRequest (request, pubGwUrl) { if (oldPath !== path && isIPFS.path(path)) { return handleRedirection({ originUrl: request.url, - redirectUrl: pathAtHttpGateway(path, pubGwUrl) + redirectUrl: pathAtHttpGateway(path, pubGwUrl), + type: request.type }) } return null @@ -670,7 +667,9 @@ function normalizedUnhandledIpfsProtocol (request, pubGwUrl) { // (will be redirected later, if needed) return handleRedirection({ originUrl: request.url, - redirectUrl: pathAtHttpGateway(path, pubGwUrl) + redirectUrl: pathAtHttpGateway(path, pubGwUrl), + type: request.type + }) } } From d50c9c62f0635f7597da322e38be8ff3826ea9f6 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Wed, 26 Jul 2023 03:57:04 -0600 Subject: [PATCH 10/19] fix(types): annotations --- add-on/src/lib/ipfs-companion.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 01fbbcaa2..e519f66f3 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -181,7 +181,7 @@ export default async function init (inQuickImport = false) { } /** - * @type {Array<{check: (request: any) => boolean, handler: (request: any, sender?: any) => Promise}>} + * @type {Array>} */ const runtimeMessageHandlers = [ { From 722882104257ba4803752e4e4e639043c4445255 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 00:38:20 -0600 Subject: [PATCH 11/19] fix(telemetry): :wastebasket: returning to previous state Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/ipfs-companion.js | 46 ++++++------------- .../lib/message-handler/IMessageHandler.ts | 6 --- 2 files changed, 14 insertions(+), 38 deletions(-) delete mode 100644 add-on/src/lib/message-handler/IMessageHandler.ts diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index e519f66f3..82a8ab2bd 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -8,6 +8,7 @@ import LRU from 'lru-cache' import pMemoize from 'p-memoize' import toMultiaddr from 'uri-to-multiaddr' import browser from 'webextension-polyfill' +import { handleConsentFromState, trackView } from '../lib/telemetry.js' import { contextMenuCopyAddressAtPublicGw, contextMenuCopyCanonicalAddress, contextMenuCopyCidAddress, contextMenuCopyPermalink, contextMenuCopyRawCid, contextMenuViewOnGateway, createContextMenus, findValueForContext } from './context-menus.js' import createCopier from './copier.js' import createDnslinkResolver from './dnslink.js' @@ -24,8 +25,6 @@ import { guiURLString, migrateOptions, optionDefaults, safeURL, storeMissingOpti import { getExtraInfoSpec } from './redirect-handler/blockOrObserve.js' import createRuntimeChecks from './runtime-checks.js' import { initState, offlinePeerCount } from './state.js' -import { handleConsentFromState, trackView } from '../lib/telemetry.js' -import RequestTracker from './message-handler/requestTracker.js' // this won't work in webworker context. Needs to be enabled manually // https://github.com/debug-js/debug/issues/916 @@ -173,6 +172,19 @@ export default async function init (inQuickImport = false) { // =================================================================== // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/sendMessage + async function onRuntimeMessage (request, sender) { + // console.log((sender.tab ? 'Message from a content script:' + sender.tab.url : 'Message from the extension'), request) + if (request.pubGwUrlForIpfsOrIpnsPath) { + const path = request.pubGwUrlForIpfsOrIpnsPath + const { validIpfsOrIpns, resolveToPublicUrl } = ipfsPathValidator + const result = await validIpfsOrIpns(path) ? await resolveToPublicUrl(path) : null + return { pubGwUrlForIpfsOrIpnsPath: result } + } + if (request.telemetry) { + return Promise.resolve(onTelemetryMessage(request.telemetry)) + } + } + function onTelemetryMessage (request) { if (request.trackView) { const { version } = browser.runtime.getManifest() @@ -180,36 +192,6 @@ export default async function init (inQuickImport = false) { } } - /** - * @type {Array>} - */ - const runtimeMessageHandlers = [ - { - check: (request) => request.pubGwUrlForIpfsOrIpnsPath, - handler: async (request) => { - const path = request.pubGwUrlForIpfsOrIpnsPath - const { validIpfsOrIpns, resolveToPublicUrl } = ipfsPathValidator - const result = await validIpfsOrIpns(path) ? await resolveToPublicUrl(path) : null - return { pubGwUrlForIpfsOrIpnsPath: result } - } - }, - { - check: (request) => request.telemetry, - handler: (request) => Promise.resolve(onTelemetryMessage(request.telemetry)) - }, - new RequestTracker('url-observed'), - new RequestTracker('url-resolved') - ] - - async function onRuntimeMessage (request, sender) { - // console.log((sender.tab ? 'Message from a content script:' + sender.tab.url : 'Message from the extension'), request) - try { - return await runtimeMessageHandlers.find(({ check }) => check(request))?.handler(request, sender) - } catch (error) { - log.error('onRuntimeMessage failed', error) - } - } - // PORTS (connection-based messaging) // =================================================================== // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/connect diff --git a/add-on/src/lib/message-handler/IMessageHandler.ts b/add-on/src/lib/message-handler/IMessageHandler.ts deleted file mode 100644 index 1ce066322..000000000 --- a/add-on/src/lib/message-handler/IMessageHandler.ts +++ /dev/null @@ -1,6 +0,0 @@ -import browser from 'webextension-polyfill' - -export interface IMessageHandler { - check: (message: T) => boolean - handler: (message: T, sender?: browser.Runtime.MessageSender) => Promise -} From 51c7dc6562773f9d1a34bfdce924ab4fe8eb612b Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 00:39:02 -0600 Subject: [PATCH 12/19] fix(telemetry): :recycle: Refactor Request Tracker Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- .../requestTracker.ts | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) rename add-on/src/lib/{message-handler => trackers}/requestTracker.ts (59%) diff --git a/add-on/src/lib/message-handler/requestTracker.ts b/add-on/src/lib/trackers/requestTracker.ts similarity index 59% rename from add-on/src/lib/message-handler/requestTracker.ts rename to add-on/src/lib/trackers/requestTracker.ts index 3d0fb2308..a6e284839 100644 --- a/add-on/src/lib/message-handler/requestTracker.ts +++ b/add-on/src/lib/trackers/requestTracker.ts @@ -1,40 +1,34 @@ +import debug from 'debug' import { trackEvent } from '../telemetry.js' -import { IMessageHandler } from './IMessageHandler.js' -interface IRequestTrackerMsg { - type: string - requestType: string -} - -export default class RequestTracker implements IMessageHandler { +export class RequestTracker { private readonly eventKey: 'url-observed' | 'url-resolved' private readonly flushInterval: number = 1000 * 60 * 5 // 5 minutes - private readonly msgKey: string = 'ipfs-companion:track-request:' + private readonly log: debug.Debugger & { error?: debug.Debugger } private lastSync: number = Date.now() private requestTypeStore: Record = {} constructor (eventKey: 'url-observed' | 'url-resolved') { this.eventKey = eventKey + this.log = debug(`ipfs-companion:request-tracker:${eventKey}`) + this.log.error = debug(`ipfs-companion:request-tracker:${eventKey}:error`) this.setupFlushScheduler() } - get checkKey (): string { - return this.msgKey + this.eventKey - } - - check ({ type }: IRequestTrackerMsg): boolean { - return type === this.checkKey - } - - async handler ({ requestType }: IRequestTrackerMsg): Promise { - if (!(requestType in this.requestTypeStore)) { - this.requestTypeStore[requestType] = 0 + async track ({ type }: { type: string }): Promise { + this.log(`track ${type}`, JSON.stringify(this.requestTypeStore)) + if (!(type in this.requestTypeStore)) { + this.requestTypeStore[type] = 0 } - this.requestTypeStore[requestType] += 1 + this.requestTypeStore[type] += 1 } private flushStore (): void { const count = Object.values(this.requestTypeStore).reduce((a, b): number => a + b, 0) + if (count === 0) { + this.log('nothing to flush') + return + } trackEvent({ key: this.eventKey, count, From dca45a284229d4fe39924b5df997c4c0505fafe6 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 00:39:28 -0600 Subject: [PATCH 13/19] fix(telemetry): hooking up requests Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/ipfs-request.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index 9f8a77806..bf273191e 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -6,10 +6,10 @@ import debug from 'debug' import isFQDN from 'is-fqdn' import isIPFS from 'is-ipfs' import LRU from 'lru-cache' -import browser from 'webextension-polyfill' import { recoveryPagePath } from './constants.js' import { braveNodeType } from './ipfs-client/brave.js' import { dropSlash, ipfsUri, pathAtHttpGateway, sameGateway } from './ipfs-path.js' +import { RequestTracker } from './trackers/requestTracker.js' import { safeURL } from './options.js' import { addRuleToDynamicRuleSetGenerator, isLocalHost, supportsBlock } from './redirect-handler/blockOrObserve.js' @@ -33,6 +33,8 @@ const recoverableHttpError = (code) => code && code >= 400 // Tracking late redirects for edge cases such as https://github.com/ipfs-shipyard/ipfs-companion/issues/436 const onHeadersReceivedRedirect = new Set() let addRuleToDynamicRuleSet = null +const observedRequestTracker = new RequestTracker('url-observed') +const resolvedRequestTracker = new RequestTracker('url-resolved') // Request modifier provides event listeners for the various stages of making an HTTP request // API Details: https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest @@ -145,10 +147,7 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida async onBeforeRequest (request) { const state = getState() if (!state.active) return - browser.runtime.sendMessage({ - type: 'ipfs-companion:track-request:url-observed', - requestType: request.type - }) + observedRequestTracker.track(request) // When local IPFS node is unreachable , show recovery page where user can redirect // to public gateway. @@ -488,10 +487,7 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida */ function handleRedirection ({ originUrl, redirectUrl, type }) { if (redirectUrl !== '' && originUrl !== '' && redirectUrl !== originUrl) { - browser.runtime.sendMessage({ - type: 'ipfs-companion:track-request:url-resolved', - requestType: type - }) + resolvedRequestTracker.track({ type }) if (supportsBlock) { return { redirectUrl } } From bf4dcb0b94dd77f91b0ee4cadd482530c11200f2 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 01:05:39 -0600 Subject: [PATCH 14/19] fix(telemetry): better types Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- add-on/src/lib/ipfs-request.js | 18 +++++++++--------- add-on/src/lib/trackers/requestTracker.ts | 11 +++++------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index bf273191e..2a400d1f7 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -9,9 +9,9 @@ import LRU from 'lru-cache' import { recoveryPagePath } from './constants.js' import { braveNodeType } from './ipfs-client/brave.js' import { dropSlash, ipfsUri, pathAtHttpGateway, sameGateway } from './ipfs-path.js' -import { RequestTracker } from './trackers/requestTracker.js' import { safeURL } from './options.js' import { addRuleToDynamicRuleSetGenerator, isLocalHost, supportsBlock } from './redirect-handler/blockOrObserve.js' +import { RequestTracker } from './trackers/requestTracker.js' const log = debug('ipfs-companion:request') log.error = debug('ipfs-companion:request:error') @@ -156,7 +156,7 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida return handleRedirection({ originUrl: request.url, redirectUrl: `${dropSlash(runtimeRoot)}${recoveryPagePath}#${encodeURIComponent(publicUri)}`, - type: request.type + request }) } @@ -168,7 +168,7 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida return handleRedirection({ originUrl: request.url, redirectUrl, - type: request.type + request }) } @@ -178,7 +178,7 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida return handleRedirection({ originUrl: request.url, redirectUrl, - type: request.type + request }) } @@ -485,9 +485,9 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida * @param {object} input contains originUrl and redirectUrl. * @returns */ -function handleRedirection ({ originUrl, redirectUrl, type }) { +function handleRedirection ({ originUrl, redirectUrl, request }) { if (redirectUrl !== '' && originUrl !== '' && redirectUrl !== originUrl) { - resolvedRequestTracker.track({ type }) + resolvedRequestTracker.track(request) if (supportsBlock) { return { redirectUrl } } @@ -547,7 +547,7 @@ async function redirectToGateway (request, url, state, ipfsPathValidator, runtim return handleRedirection({ originUrl: request.url, redirectUrl, - type: request.type + request }) } @@ -618,7 +618,7 @@ function normalizedRedirectingProtocolRequest (request, pubGwUrl) { return handleRedirection({ originUrl: request.url, redirectUrl: pathAtHttpGateway(path, pubGwUrl), - type: request.type + request }) } return null @@ -664,7 +664,7 @@ function normalizedUnhandledIpfsProtocol (request, pubGwUrl) { return handleRedirection({ originUrl: request.url, redirectUrl: pathAtHttpGateway(path, pubGwUrl), - type: request.type + request }) } diff --git a/add-on/src/lib/trackers/requestTracker.ts b/add-on/src/lib/trackers/requestTracker.ts index a6e284839..51444e299 100644 --- a/add-on/src/lib/trackers/requestTracker.ts +++ b/add-on/src/lib/trackers/requestTracker.ts @@ -1,4 +1,5 @@ import debug from 'debug' +import type browser from 'webextension-polyfill' import { trackEvent } from '../telemetry.js' export class RequestTracker { @@ -6,7 +7,7 @@ export class RequestTracker { private readonly flushInterval: number = 1000 * 60 * 5 // 5 minutes private readonly log: debug.Debugger & { error?: debug.Debugger } private lastSync: number = Date.now() - private requestTypeStore: Record = {} + private requestTypeStore: {[key in browser.WebRequest.ResourceType]?: number} = {} constructor (eventKey: 'url-observed' | 'url-resolved') { this.eventKey = eventKey @@ -15,15 +16,13 @@ export class RequestTracker { this.setupFlushScheduler() } - async track ({ type }: { type: string }): Promise { + async track ({ type }: browser.WebRequest.OnBeforeRequestDetailsType): Promise { this.log(`track ${type}`, JSON.stringify(this.requestTypeStore)) - if (!(type in this.requestTypeStore)) { - this.requestTypeStore[type] = 0 - } - this.requestTypeStore[type] += 1 + this.requestTypeStore[type] = (this.requestTypeStore[type] || 0) + 1 } private flushStore (): void { + this.log('flushing') const count = Object.values(this.requestTypeStore).reduce((a, b): number => a + b, 0) if (count === 0) { this.log('nothing to flush') From 29d5a5e9ebdebd614a6c8db2ebef0625e1b246ad Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 01:47:52 -0600 Subject: [PATCH 15/19] fix(countly): :wastebasket: more stuff goes, because test need to pass. Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- patches/countly-sdk-web+23.2.2.patch | 106 ++++++++++++++++----------- 1 file changed, 63 insertions(+), 43 deletions(-) diff --git a/patches/countly-sdk-web+23.2.2.patch b/patches/countly-sdk-web+23.2.2.patch index a3f99907e..194da1e4d 100644 --- a/patches/countly-sdk-web+23.2.2.patch +++ b/patches/countly-sdk-web+23.2.2.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/countly-sdk-web/lib/countly.js b/node_modules/countly-sdk-web/lib/countly.js -index da26eb6..53c31d0 100644 +index da26eb6..0bf0ec8 100644 --- a/node_modules/countly-sdk-web/lib/countly.js +++ b/node_modules/countly-sdk-web/lib/countly.js @@ -52,10 +52,22 @@ @@ -27,7 +27,7 @@ index da26eb6..53c31d0 100644 // TODO: check if logging can be added here, like: // console.error("Not running in browser"); return; -@@ -347,12 +359,12 @@ +@@ -347,23 +359,16 @@ checkIgnore(); @@ -39,11 +39,23 @@ index da26eb6..53c31d0 100644 } catch (ex) { - log(logLevelEnums.ERROR, "initialize, Could not parse name: " + window.name + ", error: " + ex); +- } +- } +- else if (location.hash && location.hash.indexOf("#cly:") === 0) { +- try { +- this.passed_data = JSON.parse(location.hash.replace("#cly:", "")); +- } +- catch (ex) { +- log(logLevelEnums.ERROR, "initialize, Could not parse hash: " + location.hash + ", error: " + ex); + log(logLevelEnums.ERROR, "initialize, Could not parse name: " + globalThis.name + ", error: " + ex); } } - else if (location.hash && location.hash.indexOf("#cly:") === 0) { -@@ -1028,7 +1040,7 @@ + ++ + if ((this.passed_data && this.passed_data.app_key && this.passed_data.app_key === this.app_key) || (this.passed_data && !this.passed_data.app_key && global)) { + if (this.passed_data.token && this.passed_data.purpose) { + if (this.passed_data.token !== getValueFromStorage("cly_old_token")) { +@@ -1028,7 +1033,7 @@ if (this.enableOrientationTracking) { // report orientation this.report_orientation(); @@ -52,7 +64,7 @@ index da26eb6..53c31d0 100644 self.report_orientation(); }); } -@@ -1618,11 +1630,11 @@ +@@ -1618,11 +1623,11 @@ log(logLevelEnums.INFO, "track_errors, Started tracking errors"); // Indicated that for this instance of the countly error tracking is enabled Countly.i[this.app_key].tracking_crashes = true; @@ -67,7 +79,7 @@ index da26eb6..53c31d0 100644 // old browsers like IE 10 and Safari 9 won't give this value 'err' to us, but if it is provided we can trigger error recording immediately if (err !== undefined && err !== null) { // false indicates fatal error (as in non_fatal:false) -@@ -1630,7 +1642,7 @@ +@@ -1630,7 +1635,7 @@ } // fallback if no error object is present for older browsers, we create it instead else { @@ -76,7 +88,7 @@ index da26eb6..53c31d0 100644 var error = ""; if (typeof msg !== "undefined") { error += msg + "\n"; -@@ -1666,7 +1678,7 @@ +@@ -1666,7 +1671,7 @@ }; // error handling for 'uncaught rejections' @@ -85,7 +97,7 @@ index da26eb6..53c31d0 100644 // true indicates non fatal error (as in non_fatal: true) dispatchErrors(new Error("Unhandled rejection (reason: " + (event.reason && event.reason.stack ? event.reason.stack : event.reason) + ")."), true); }); -@@ -1909,13 +1921,13 @@ +@@ -1909,13 +1914,13 @@ this.begin_session(); this.start_time(); // end session on unload @@ -101,7 +113,7 @@ index da26eb6..53c31d0 100644 var hidden = "hidden"; /** -@@ -1931,17 +1943,17 @@ +@@ -1931,17 +1936,17 @@ } // add focus handling eventListeners @@ -125,7 +137,7 @@ index da26eb6..53c31d0 100644 } // Page Visibility API for changing tabs and minimizing browser -@@ -1971,10 +1983,10 @@ +@@ -1971,10 +1976,10 @@ inactivityCounter = 0; } @@ -140,7 +152,7 @@ index da26eb6..53c31d0 100644 // track user inactivity setInterval(function() { -@@ -2048,7 +2060,7 @@ +@@ -2048,7 +2053,7 @@ // truncate new segment segments = truncateObject(segments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "track_pageview", log); if (this.track_domains) { @@ -149,7 +161,7 @@ index da26eb6..53c31d0 100644 } if (useSessionCookie) { -@@ -2071,7 +2083,7 @@ +@@ -2071,7 +2076,7 @@ else if (typeof document.referrer !== "undefined" && document.referrer.length) { var matches = urlParseRE.exec(document.referrer); // do not report referrers of current website @@ -158,7 +170,7 @@ index da26eb6..53c31d0 100644 segments.start = 1; } } -@@ -2147,7 +2159,7 @@ +@@ -2147,7 +2152,7 @@ // truncate new segment segments = truncateObject(segments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "processClick", log); if (self.track_domains) { @@ -167,7 +179,7 @@ index da26eb6..53c31d0 100644 } add_cly_events({ key: internalEventKeyEnums.ACTION, -@@ -2173,7 +2185,7 @@ +@@ -2173,7 +2178,7 @@ if (parent) { log(logLevelEnums.INFO, "track_scrolls, Tracking the specified children"); } @@ -176,7 +188,7 @@ index da26eb6..53c31d0 100644 isScrollRegistryOpen = true; trackingScrolls = true; -@@ -2813,8 +2825,8 @@ +@@ -2813,8 +2818,8 @@ else { var pages = widgets[i].target_pages; for (var k = 0; k < pages.length; k++) { @@ -187,7 +199,7 @@ index da26eb6..53c31d0 100644 var isContainAsterisk = pages[k].includes("*"); if (((isContainAsterisk && isWildcardMatched) || isFullPathMatched) && !widgets[i].hide_sticker) { processWidget(widgets[i], true); -@@ -3048,7 +3060,7 @@ +@@ -3048,7 +3053,7 @@ return; } @@ -196,7 +208,7 @@ index da26eb6..53c31d0 100644 var feedbackWidgetFamily; // set feedback widget family as ratings and load related style file when type is ratings -@@ -3170,7 +3182,7 @@ +@@ -3170,7 +3175,7 @@ wrapper.appendChild(iframe); log(logLevelEnums.DEBUG, "present_feedback_widget, Appended the iframe"); @@ -205,7 +217,7 @@ index da26eb6..53c31d0 100644 var data = {}; try { data = JSON.parse(e.data); -@@ -3249,9 +3261,9 @@ +@@ -3249,9 +3254,9 @@ break; case "onScrollHalfwayDown": @@ -217,7 +229,7 @@ index da26eb6..53c31d0 100644 var documentHeight = getDocHeight(); if (scrollY >= (documentHeight / 2)) { surveyShown = true; -@@ -3841,13 +3853,13 @@ +@@ -3841,13 +3846,13 @@ var height = (screen.height) ? parseInt(screen.height) : 0; if (width !== 0 && height !== 0) { var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); @@ -235,7 +247,7 @@ index da26eb6..53c31d0 100644 // we have landscape orientation // switch values for all except ios var temp = width; -@@ -3860,8 +3872,8 @@ +@@ -3860,8 +3865,8 @@ } // getting density ratio @@ -246,7 +258,7 @@ index da26eb6..53c31d0 100644 } // getting locale -@@ -3873,7 +3885,7 @@ +@@ -3873,7 +3878,7 @@ if (typeof document.referrer !== "undefined" && document.referrer.length) { var matches = urlParseRE.exec(document.referrer); // do not report referrers of current website @@ -255,7 +267,7 @@ index da26eb6..53c31d0 100644 var ignoring = false; if (ignoreReferrers && ignoreReferrers.length) { for (var k = 0; k < ignoreReferrers.length; k++) { -@@ -3907,7 +3919,7 @@ +@@ -3907,7 +3912,7 @@ * @memberof Countly._internals */ function log(level, message) { @@ -264,7 +276,7 @@ index da26eb6..53c31d0 100644 // parse the arguments into a string if it is an object if (arguments[2] && typeof arguments[2] === "object") { arguments[2] = JSON.stringify(arguments[2]); -@@ -3963,72 +3975,62 @@ +@@ -3963,72 +3968,62 @@ */ function sendXmlHttpRequest(functionName, url, params, callback, useBroadResponseValidator) { useBroadResponseValidator = useBroadResponseValidator || false; @@ -364,12 +376,12 @@ index da26eb6..53c31d0 100644 - }; - if (method === "GET") { - xhr.send(); +- } +- else { +- xhr.send(data); + } else { + throw new Error(response.statusText); } -- else { -- xhr.send(data); -- } - } - catch (e) { + }).catch(function (e) { @@ -384,7 +396,7 @@ index da26eb6..53c31d0 100644 } /** -@@ -4105,7 +4107,7 @@ +@@ -4105,7 +4100,7 @@ * */ function processScroll() { @@ -393,7 +405,7 @@ index da26eb6..53c31d0 100644 } /** -@@ -4131,7 +4133,7 @@ +@@ -4131,7 +4126,7 @@ // truncate new segment segments = truncateObject(segments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "processScrollView", log); if (self.track_domains) { @@ -402,7 +414,7 @@ index da26eb6..53c31d0 100644 } add_cly_events({ key: internalEventKeyEnums.ACTION, -@@ -4862,7 +4864,7 @@ +@@ -4862,7 +4857,7 @@ */ var get_event_target = function(event) { if (!event) { @@ -411,7 +423,7 @@ index da26eb6..53c31d0 100644 } if (typeof event.target !== "undefined") { return event.target; -@@ -4924,7 +4926,7 @@ +@@ -4924,7 +4919,7 @@ var device = "desktop"; // regexps corresponding to tablets or phones that can be found in userAgent string @@ -420,7 +432,7 @@ index da26eb6..53c31d0 100644 var phoneCheck = /(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|nintendo ds|archos|skyfire|puffin|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard)/; // check whether the regexp values corresponds to something in the user agent string -@@ -5009,7 +5011,7 @@ +@@ -5009,7 +5004,7 @@ return Math.min( Math.min(D.body.clientHeight, D.documentElement.clientHeight), Math.min(D.body.offsetHeight, D.documentElement.offsetHeight), @@ -429,7 +441,7 @@ index da26eb6..53c31d0 100644 ); } -@@ -5018,13 +5020,13 @@ +@@ -5018,22 +5013,9 @@ * @returns {String} device orientation */ function getOrientation() { @@ -437,15 +449,23 @@ index da26eb6..53c31d0 100644 + return globalThis.innerWidth > globalThis.innerHeight ? "landscape" : "portrait"; } - /** - * Monitor parallel storage changes like other opened tabs - */ +- /** +- * Monitor parallel storage changes like other opened tabs +- */ - window.addEventListener("storage", function(e) { -+ globalThis.addEventListener("storage", function(e) { - var parts = (e.key + "").split("/"); - var key = parts.pop(); - var appKey = parts.pop(); -@@ -5171,7 +5173,7 @@ +- var parts = (e.key + "").split("/"); +- var key = parts.pop(); +- var appKey = parts.pop(); +- +- if (Countly.i && Countly.i[appKey]) { +- Countly.i[appKey].onStorageChange(key, e.newValue); +- } +- }); +- + /** + * Load external js files + * @param {String} tag - Tag/node name to load file in +@@ -5171,7 +5153,7 @@ * @return {string} view name * */ Countly.getViewName = function() { @@ -454,7 +474,7 @@ index da26eb6..53c31d0 100644 }; /** -@@ -5179,7 +5181,7 @@ +@@ -5179,7 +5161,7 @@ * @return {string} view url * */ Countly.getViewUrl = function() { @@ -463,12 +483,12 @@ index da26eb6..53c31d0 100644 }; /** -@@ -5187,7 +5189,7 @@ +@@ -5187,7 +5169,7 @@ * @return {string} view url * */ Countly.getSearchQuery = function() { - return window.location.search; -+ return globalThis.location.search; ++ return ''; }; /** From d2dfc5022f873c41f341b68483e3db85dba73701 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 01:56:30 -0600 Subject: [PATCH 16/19] fix(lint): fixed --- add-on/src/lib/trackers/requestTracker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/add-on/src/lib/trackers/requestTracker.ts b/add-on/src/lib/trackers/requestTracker.ts index 51444e299..b9612fadd 100644 --- a/add-on/src/lib/trackers/requestTracker.ts +++ b/add-on/src/lib/trackers/requestTracker.ts @@ -7,7 +7,7 @@ export class RequestTracker { private readonly flushInterval: number = 1000 * 60 * 5 // 5 minutes private readonly log: debug.Debugger & { error?: debug.Debugger } private lastSync: number = Date.now() - private requestTypeStore: {[key in browser.WebRequest.ResourceType]?: number} = {} + private requestTypeStore: { [key in browser.WebRequest.ResourceType]?: number } = {} constructor (eventKey: 'url-observed' | 'url-resolved') { this.eventKey = eventKey @@ -18,7 +18,7 @@ export class RequestTracker { async track ({ type }: browser.WebRequest.OnBeforeRequestDetailsType): Promise { this.log(`track ${type}`, JSON.stringify(this.requestTypeStore)) - this.requestTypeStore[type] = (this.requestTypeStore[type] || 0) + 1 + this.requestTypeStore[type] = (this.requestTypeStore[type] ?? 0) + 1 } private flushStore (): void { From c954c289a85df30efdf14daf320abd61f504782d Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 02:52:31 -0600 Subject: [PATCH 17/19] feat(test): test tracker. --- add-on/src/lib/trackers/requestTracker.ts | 7 +- .../lib/trackers/requestTrackers.test.ts | 116 ++++++++++++++++++ 2 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 test/functional/lib/trackers/requestTrackers.test.ts diff --git a/add-on/src/lib/trackers/requestTracker.ts b/add-on/src/lib/trackers/requestTracker.ts index b9612fadd..4f3a97009 100644 --- a/add-on/src/lib/trackers/requestTracker.ts +++ b/add-on/src/lib/trackers/requestTracker.ts @@ -4,19 +4,20 @@ import { trackEvent } from '../telemetry.js' export class RequestTracker { private readonly eventKey: 'url-observed' | 'url-resolved' - private readonly flushInterval: number = 1000 * 60 * 5 // 5 minutes + private readonly flushInterval: number private readonly log: debug.Debugger & { error?: debug.Debugger } private lastSync: number = Date.now() private requestTypeStore: { [key in browser.WebRequest.ResourceType]?: number } = {} - constructor (eventKey: 'url-observed' | 'url-resolved') { + constructor (eventKey: 'url-observed' | 'url-resolved', flushInterval = 1000 * 60 * 5) { this.eventKey = eventKey this.log = debug(`ipfs-companion:request-tracker:${eventKey}`) this.log.error = debug(`ipfs-companion:request-tracker:${eventKey}:error`) + this.flushInterval = flushInterval this.setupFlushScheduler() } - async track ({ type }: browser.WebRequest.OnBeforeRequestDetailsType): Promise { + track ({ type }: browser.WebRequest.OnBeforeRequestDetailsType): void { this.log(`track ${type}`, JSON.stringify(this.requestTypeStore)) this.requestTypeStore[type] = (this.requestTypeStore[type] ?? 0) + 1 } diff --git a/test/functional/lib/trackers/requestTrackers.test.ts b/test/functional/lib/trackers/requestTrackers.test.ts new file mode 100644 index 000000000..d0c091100 --- /dev/null +++ b/test/functional/lib/trackers/requestTrackers.test.ts @@ -0,0 +1,116 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import browser from 'sinon-chrome'; +import PatchedCountly from 'countly-sdk-web' +import { RequestTracker } from './../../../../add-on/src/lib/trackers/requestTracker.js' + +const sinonSandBox = sinon.createSandbox() +describe.only('lib/trackers/requestTracker', () => { + + let requestTracker: RequestTracker + let countlySDKStub: sinon.SinonStub + let clock: sinon.SinonFakeTimers + + before(() => { + clock = sinonSandBox.useFakeTimers() + countlySDKStub = sinonSandBox.stub(PatchedCountly) + }) + + afterEach(() => { + sinonSandBox.resetHistory() + }) + + describe('url-observed', () => { + before(() => { + requestTracker = new RequestTracker('url-observed') + }) + + it('should init a Tracker', () => { + expect(requestTracker).to.be.instanceOf(RequestTracker) + expect(requestTracker).to.have.property('track') + }) + + it('should track a request', async () => { + await requestTracker.track({ type: 'main_frame' } as browser.WebRequest.OnBeforeRequestDetailsType) + clock.tick(1000 * 60 * 6) + sinon.assert.calledWith(countlySDKStub.add_event, { + key: 'url-observed', + count: 1, + dur: 300000, + segmentation: { + main_frame: 1 + } + }) + }) + + it('should track multiple requests', async () => { + await requestTracker.track({ type: 'main_frame' } as browser.WebRequest.OnBeforeRequestDetailsType) + await requestTracker.track({ type: 'sub_frame' } as browser.WebRequest.OnBeforeRequestDetailsType) + await requestTracker.track({ type: 'xmlHTTPRequest' } as browser.WebRequest.OnBeforeRequestDetailsType) + clock.tick(1000 * 60 * 6) + sinon.assert.calledWith(countlySDKStub.add_event, { + key: 'url-observed', + count: 3, + dur: 300000, + segmentation: { + main_frame: 1, + sub_frame: 1, + xmlHTTPRequest: 1 + } + }) + }) + + it('should not send event if count is 0', async () => { + clock.tick(1000 * 60 * 6) + + sinon.assert.notCalled(countlySDKStub.add_event) + }) + }) + + describe('url-resolved', () => { + before(() => { + requestTracker = new RequestTracker('url-resolved') + }) + + it('should init a Tracker', () => { + expect(requestTracker).to.be.instanceOf(RequestTracker) + expect(requestTracker).to.have.property('track') + }) + + it('should track a request', async () => { + await requestTracker.track({ type: 'main_frame' } as browser.WebRequest.OnBeforeRequestDetailsType) + clock.tick(1000 * 60 * 6) + sinon.assert.calledWith(countlySDKStub.add_event, { + key: 'url-resolved', + count: 1, + dur: 300000, + segmentation: { + main_frame: 1 + } + }) + }) + + it('should track multiple requests', async () => { + await requestTracker.track({ type: 'main_frame' } as browser.WebRequest.OnBeforeRequestDetailsType) + await requestTracker.track({ type: 'sub_frame' } as browser.WebRequest.OnBeforeRequestDetailsType) + await requestTracker.track({ type: 'xmlHTTPRequest' } as browser.WebRequest.OnBeforeRequestDetailsType) + clock.tick(1000 * 60 * 6) + sinon.assert.calledWith(countlySDKStub.add_event, { + key: 'url-resolved', + count: 3, + dur: 300000, + segmentation: { + main_frame: 1, + sub_frame: 1, + xmlHTTPRequest: 1 + } + }) + }) + + it('should not send event if count is 0', async () => { + clock.tick(1000 * 60 * 6) + + sinon.assert.notCalled(countlySDKStub.add_event) + }) + }) +}) From 5af01254e0f037206cc7ddb2b484a2171097e83b Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 02:58:50 -0600 Subject: [PATCH 18/19] fix: remove only Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- test/functional/lib/trackers/requestTrackers.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/lib/trackers/requestTrackers.test.ts b/test/functional/lib/trackers/requestTrackers.test.ts index d0c091100..0e0b936c1 100644 --- a/test/functional/lib/trackers/requestTrackers.test.ts +++ b/test/functional/lib/trackers/requestTrackers.test.ts @@ -5,7 +5,7 @@ import PatchedCountly from 'countly-sdk-web' import { RequestTracker } from './../../../../add-on/src/lib/trackers/requestTracker.js' const sinonSandBox = sinon.createSandbox() -describe.only('lib/trackers/requestTracker', () => { +describe('lib/trackers/requestTracker', () => { let requestTracker: RequestTracker let countlySDKStub: sinon.SinonStub From 8c9e4275c878461147900709a08b377b17d3aa24 Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 28 Jul 2023 14:08:36 -0600 Subject: [PATCH 19/19] fix: :lipstick: line break Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- docs/telemetry/COLLECTED_DATA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/telemetry/COLLECTED_DATA.md b/docs/telemetry/COLLECTED_DATA.md index 56e1570c4..ffb970f7d 100644 --- a/docs/telemetry/COLLECTED_DATA.md +++ b/docs/telemetry/COLLECTED_DATA.md @@ -17,7 +17,7 @@ Telemetry is sent to Countly instance at `countly.ipfs.tech`. You can read how t As a general rule, we collect only application data; no user data. Some examples of application data we collect are: | Metric data name | Metric feature name | Metric trigger | Analytics use | Notes | -|:-------------------:|---------------------|---------------------------------------------------------|-------------------------------------------------------------------|---------------| | | +|:-------------------:|---------------------|---------------------------------------------------------|-------------------------------------------------------------------|---------------| | view:welcome | views | When the welcome view is shown | View count | | | view:options | views | When the options view is shown | View count | | | view:quick-import | views | When the quick-import view is shown | View count | |