- // ==UserScript==
- // @namespace jp.sceneq.rgtuwaaa
- // @name remove google tracking UWAA
- // @description remove google tracking
- // @homepageURL https://github.com/sceneq/RemoveGoogleTracking
- // @version 0.21
- // @include https://www.google.*/*
- // @grant none
- // @run-at document-body
- // @author sceneq
- // @license MIT
- // ==/UserScript==
-
- const yes = () => true;
- const doNothing = () => {};
- const $ = (s, n = document) => n.querySelector(s);
- const $$ = (s, n = document) => [...n.querySelectorAll(s)];
- const sleep = (millis) => new Promise((resolve) => setTimeout(resolve, millis));
- const zip = (rows) => rows[0].map((_, c) => rows.map((row) => row[c]));
-
- const rewriteProperties = (arg) => {
- for (const [obj, prop, value] of arg) {
- if (!obj) continue;
- Object.defineProperty(obj, prop, {
- value,
- writable: false,
- });
- }
- };
-
- const waitUntilDeclare = (obj, property, option = { interval: 100 }) => {
- console.debug('waitUntilDeclare Start', obj.toString(), property);
- return new Promise(async (resolve, reject) => {
- const propertyNames = property.split('.');
- let currObj = obj;
- for (const propertyName of propertyNames) {
- while (!(propertyName in currObj) || currObj[propertyName] === null) {
- await sleep(option.interval);
- }
- currObj = currObj[propertyName];
- }
- console.debug('waitUntilDeclare Done', obj.toString(), property);
- resolve(currObj);
- });
- };
-
- const waitUntilNode = (sel, option = { interval: 100 }) => {
- console.debug('waitUntilNode Start', sel);
- return new Promise(async (resolve, reject) => {
- let d = null;
- while (d === null) {
- d = $(sel);
- await sleep(option.interval);
- }
- console.debug('waitUntilNode Done', sel);
- resolve(d);
- });
- };
-
- const untrackBuilder = (arg) => {
- const badParams = arg.badParams;
- return (a) => {
- const href = a?.href;
- if (!href) return;
- const url = new URL(href);
- if (a.getAttribute('href') === '/url') {
- a.href = url.searchParams.get('url'); // todo q?
- } else {
- a.removeAttribute('ping');
- a.href = delParams(href, badParams);
- }
- };
- };
-
- const delParams = (sUrl, params) => {
- if (sUrl.startsWith('/')) {
- sUrl = location.origin + sUrl;
- }
- const url = new URL(sUrl);
- const keys = [...url.searchParams.keys()];
- for (const k of keys) {
- if (!params.includes(k)) continue;
- url.searchParams.delete(k);
- }
- return url.toString();
- };
-
- const Types = {
- search: Symbol('search'),
- isch: Symbol('image'),
- shop: Symbol('shop'),
- nws: Symbol('news'),
- vid: Symbol('video'),
- bks: Symbol('book'),
- maps: Symbol('maps'),
- fin: Symbol('finance'),
- toppage: Symbol('toppage'),
- // flights: Symbol('flights'),
- };
-
- const tbmToType = (tbm) => {
- if (tbm === null) {
- return Types.search;
- }
- const t = {
- isch: Types.isch,
- shop: Types.shop,
- nws: Types.nws,
- vid: Types.vid,
- bks: Types.bks,
- fin: Types.fin,
- }[tbm];
- if (t === undefined) {
- return null;
- } else {
- return t;
- }
- };
-
- const BadParamsBase = [
- 'biw', // offsetWidth
- 'bih', // offsetHeight
- 'ei',
- 'sa',
- 'ved',
- 'source',
- 'prds',
- 'bvm',
- 'bav',
- 'psi',
- 'stick',
- 'dq',
- 'ech',
- 'gs_gbg',
- 'gs_rn',
- 'cp',
- 'ictx',
- 'cshid',
- 'gs_lcp',
- ];
-
- const BadParams = (() => {
- const o = {};
- o[Types.search] = [
- 'vet',
- 'pbx',
- 'dpr',
- 'pf',
- 'gs_rn',
- 'gs_mss',
- 'pq',
- 'cp',
- 'oq',
- 'sclient',
- 'gs_l',
- 'aqs',
- 'sxsrf',
- ];
- o[Types.isch] = [
- 'vet',
- 'scroll',
- 'yv',
- 'iact',
- 'forward',
- 'ndsp',
- 'csi',
- 'tbnid',
- 'sclient',
- 'oq',
- ];
- o[Types.shop] = ['oq'];
- o[Types.nws] = ['oq'];
- o[Types.vid] = ['oq'];
- o[Types.bks] = ['oq'];
- o[Types.fin] = ['oq'];
- o[Types.toppage] = ['oq', 'sclient', 'uact'];
- o[Types.maps] = ['psi'];
- return o;
- })();
-
- const overwrite = (arg) => {
- const badImageSrcRegex = /\/(?:(?:gen(?:erate)?|client|fp)_|log)204|(?:metric|csi)\.gstatic\.|(?:adservice)\.(google)/;
- if(arg.pageType.ty !== Types.maps){
- Object.defineProperty(window.Image.prototype, 'src', {
- set: function (url) {
- //console.debug('img send', url);
- if (badImageSrcRegex.test(url)) return;
- this.setAttribute('src', url);
- },
- });
- }
-
- Object.defineProperty(window.HTMLScriptElement.prototype, 'src', {
- set: function (url) {
- //console.debug('script send', url);
- if(typeof(url) === "string"){
- this.setAttribute('src', delParams(url, arg.badParams));
- } else {
- this.setAttribute('src', url);
- }
- },
- });
-
- const blockOnOpenPaths = [
- '/imgevent',
- '/async/ecr',
- '/async/bgasy',
- '/shopping/product/.+?/popout',
- '/_/VisualFrontendUi/browserinfo',
- '/_/VisualFrontendUi/jserror',
- ];
-
- // todo fakeXHR?
- const blockOnSendPaths = ['/log'];
-
- const regBlockOnOpenPaths = new RegExp(
- '^(?:' + blockOnOpenPaths.join('|') + ')'
- );
- const regBlockOnSendPaths = new RegExp(
- '^(?:' + blockOnSendPaths.join('|') + ')'
- );
-
- const origOpen = window.XMLHttpRequest.prototype.open;
- window.XMLHttpRequest.prototype.open = function (act, path) {
- try {
- this.__path = path;
- this.__url = null;
- if (path === undefined) return;
- if (path.startsWith('https://')) {
- const url = new URL(path);
- this.__url = url;
- if (this.__url.origin === 'aa.google.com') return;
- if (regBlockOnOpenPaths.test(this.__url.pathname)) return;
- } else if (regBlockOnOpenPaths.test(this.__path)) {
- return;
- }
- const new_path = delParams(path, arg.badParams);
- } catch (e) {
- console.error(e);
- return;
- }
- //console.debug('xhr open', this.__path);
- return origOpen.apply(this, [act, this.__path]);
- };
-
- const origSend = window.XMLHttpRequest.prototype.send;
- window.XMLHttpRequest.prototype.send = function (body) {
- try {
- if (this.__url !== null) {
- if (regBlockOnSendPaths.test(this.__url.pathname)) return;
- } else if (regBlockOnOpenPaths.test(this.__path)) {
- return;
- }
- } catch (e) {
- console.error(e);
- return;
- }
- //console.debug('xhr send', this.__path);
- return origSend.apply(this, [body]);
- };
-
- if ('navigator' in window) {
- const origSendBeacon = navigator.sendBeacon.bind(navigator);
- navigator.sendBeacon = (path, data) => {
- if (path === undefined) return;
- if (path.startsWith('https://play.google.com/log')) return;
- if (badImageSrcRegex.test(path)) return;
- //console.debug('nav send', path);
- origSendBeacon(path, data);
- };
- }
- };
-
- const searchFormUriuri = async (arg) => {
- // TODO mobile, mobileOld
- let form = null;
- if (arg.pageType.mobileOld) {
- form = $('#sf');
- } else if (arg.pageType.ty === Types.isch) {
- form = await waitUntilDeclare(window, 'sf');
- } else {
- form = await waitUntilDeclare(window, 'tsf');
- }
-
- if (form === null) {
- console.warn('form === null');
- return;
- }
-
- for (const i of form) {
- if (i.tagName !== 'INPUT') continue;
- if (arg.badParams.includes(i.name)) {
- i.parentElement.removeChild(i);
- }
- }
- const orig = form.appendChild.bind(form);
- form.appendChild = (e) => {
- if (!arg.badParams.includes(e.name)) {
- orig(e);
- }
- };
- };
-
- const untrackAnchors = (untrack, arg) => {
- let property = null;
- if (arg.pageType.mobile) {
- property = 'topstuff';
- } else if (arg.pageType.mobileOld) {
- property = 'main'; // 'rmenu';
- } else if (arg.pageType.ty === Types.search) {
- property = 'hdtb-msb';
- } else if (arg.pageType.ty === Types.bks) {
- property = 'lr_';
- } else if (arg.pageType.ty === Types.vid) {
- property = 'hdtb-msb';
- } else if (arg.pageType.ty === Types.nws) {
- property = 'hdtb-msb';
- } else if (arg.pageType.ty === Types.isch) {
- property = 'i4';
- } else {
- property = 'search';
- }
-
- return waitUntilDeclare(window, property).then((_) => {
- for (const a of $$('a')) {
- untrack(a);
- }
- });
- };
-
- const gcommon = async (arg) => {
- const untrack = untrackBuilder(arg);
- const p1 = waitUntilDeclare(window, 'google').then((google) => {
- rewriteProperties([
- [google, 'log', yes],
- [google, 'logUrl', doNothing],
- [google, 'getLEI', yes],
- [google, 'ctpacw', yes],
- [google, 'csiReport', yes],
- [google, 'report', yes],
- [google, 'aft', yes],
- //[google, 'kEI', '0'],
- //[google, 'getEI', yes], or ()=>"0"
- ]);
- });
- rewriteProperties([
- [window, 'rwt', doNothing],
- [window.gbar_, 'Rm', doNothing],
- ]);
-
- const p2 = untrackAnchors(untrack, arg);
- const p3 = searchFormUriuri(arg);
- await Promise.all([p1, p2, p3]);
-
- if (arg.pageType.mobile) {
- const sel =
- arg.pageType.ty === Types.search
- ? '#bres + h1 + div > div + div'
- : '#bres + div > div + div';
- new MutationObserver((mutations) => {
- const nodes = mutations.flatMap((d) => [...d.addedNodes]);
- for (const n of nodes) {
- new MutationObserver((_, obs) => {
- console.debug('untrack', n);
- for (const a of $$('a', n)) {
- untrack(a);
- }
- obs.disconnect();
- }).observe(n, { childList: true });
- }
- }).observe($(sel), { childList: true });
- }
- };
-
- const fun = {};
-
- fun[Types.toppage] = searchFormUriuri;
-
- fun[Types.search] = gcommon;
- fun[Types.vid] = gcommon;
- fun[Types.nws] = gcommon;
- fun[Types.bks] = gcommon;
- fun[Types.fin] = gcommon;
-
- fun[Types.isch] = async (arg) => {
- // TODO mobile, mobileOld
- const untrack = untrackBuilder(arg);
- const p1 = untrackAnchors(untrack, arg);
- const p2 = searchFormUriuri(arg);
- const p3 = waitUntilDeclare(window, 'islrg').then((islrg) => {
- const imagesWrapper = islrg.children[0];
- const uuuu = (div) => {
- if (div.children.length !== 2) return;
- const a = div.children[1];
- untrack(a);
- a.removeAttribute('jsaction');
- };
- for (const div of $$(':scope > div', imagesWrapper)) {
- uuuu(div);
- }
- new MutationObserver((mutations) => {
- for (const div of mutations.flatMap((m) => [...m.addedNodes])) {
- uuuu(div);
- }
- }).observe(imagesWrapper, { childList: true });
- });
-
- /*
- const p4 = waitUntilNode('.ZsbmCf').then(_ => {
- for (const a of $$('.ZsbmCf, .Beeb4e')) {
- const ee = e => untrack(a);
- a.addEventListener('click', ee);
- a.addEventListener('contextmenu', ee);
- }
- });
- */
-
- /*
- const safeurl = a => {
- const s = a.j ?? a;
- if (s.startsWith('/imgres?')) {
- return delParams(location.origin + s, [
- 'vet',
- 'w',
- 'h',
- 'ved',
- // q imgurl imgrefurl tbnid docid
- ]);
- }
- if (s.startsWith('/')) return s;
- return new URLSearchParams(s).get('url') ?? s;
- };
- const p4 = waitUntilDeclare(window, 'default_VisualFrontendUi').then(_ => {
- rewriteProperties([[_, 'zd', safeurl]]);
- });
- */
- await Promise.all([p1, p2, p3]);
- };
-
- fun[Types.shop] = async (arg) => {
- // TODO desktop, mobile, mobileOld
- const untrack = untrackBuilder(arg);
- const p1 = untrackAnchors(untrack, arg);
- const p2 = searchFormUriuri(arg);
- const p3 = waitUntilDeclare(window, 'google.pmc.spop.r', {
- interval: 30,
- }).then((shopObj) => {
- for (const result of $$('.sh-dlr__list-result')) {
- const shop = shopObj[result.dataset.docid];
- const link = shop[34][6];
- result.querySelector("a[class$='__merchant-name']").href = link;
- result.querySelector('a.translate-content').href = link;
- result.querySelector('div.sh-dlr__thumbnail > a').href = link;
- shop[3][0][1] = link;
- shop[14][1] = link;
- shop[89][16] = link;
- shop[89][18][0] = link;
- if (shop[85] !== null) {
- shop[85][3] = link;
- }
- }
- });
- await Promise.all([p1, p2, p3]);
- };
-
- fun[Types.maps] = async (arg) => {
- // TODO desktop, mobile, mobileOld
- const untrack = (a) => {
- a.addEventListener('click', (e) => e.stopPropagation());
- for (const n of [...a.attributes]
- .map((at) => at.name)
- .filter((n) => ['href', 'class'].indexOf(n) === -1)) {
- a.removeAttribute(n);
- }
- };
- const main = await waitUntilNode('div[role=main]', { interval: 30 });
- new MutationObserver((mutations) => {
- console.log(mutations);
- }).observe(main.children[1].children[0], { childList: true });
- };
-
- (async () => {
- 'use strict';
- console.debug('rgt: init');
- console.time('rgt');
-
- const ty = (() => {
- if (location.pathname.startsWith('/maps')) {
- return Types.maps;
- }
- if (location.pathname === '/' || location.pathname === '/webhp') {
- return Types.toppage;
- }
- const tbm = new URLSearchParams(location.search).get('tbm');
- if (location.pathname === '/search') {
- return tbmToType(tbm);
- }
- return null;
- })();
-
- if (ty === null) {
- console.debug('ty === null', location.href);
- return;
- }
-
- const badParams = (() => {
- return [...BadParamsBase, ...BadParams[ty]];
- })();
-
- if (ty in fun) {
- const mobileOld = $('html[itemtype]') === null; // &liteui=2
- const mobile = !mobileOld && $('meta[name=viewport]') !== null;
- const arg = {
- pageType: {
- ty,
- mobile,
- mobileOld,
- },
- badParams,
- };
- console.debug('arg', arg);
- overwrite(arg);
- await fun[ty](arg);
- } else {
- console.warn(`key not found in fun: ${ty.toString()}`);
- }
- console.timeEnd('rgt');
- })();