12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562 |
- /*******************************************************************************
- ηMatrix - a browser extension to black/white list requests.
- Copyright (C) 2014-2019 Raymond Hill
- Copyright (C) 2019-2022 Alessio Vanni
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see {http://www.gnu.org/licenses/}.
- Home: https://gitlab.com/vannilla/ematrix
- uMatrix Home: https://github.com/gorhill/uMatrix
- */
- 'use strict';
- (function () {
- Cu.import('chrome://ematrix/content/lib/Punycode.jsm');
- // Stuff which is good to do very early so as to avoid visual glitches.
- (function () {
- let paneContentPaddingTop =
- vAPI.localStorage.getItem('paneContentPaddingTop');
- let touchDevice = vAPI.localStorage.getItem('touchDevice');
- if (typeof paneContentPaddingTop === 'string') {
- document
- .querySelector('.paneContent')
- .style
- .setProperty('padding-top',
- paneContentPaddingTop);
- }
- /* This is for CSS */
- if (touchDevice === 'true') {
- document.body.setAttribute('data-touch', 'true');
- } else {
- document.addEventListener('touchstart', function onTouched(ev) {
- document.removeEventListener(ev.type, onTouched);
- document.body.setAttribute('data-touch', 'true');
- vAPI.localStorage.setItem('touchDevice', 'true');
- resizePopup();
- });
- }
- })();
- let popupWasResized = function () {
- document.body.setAttribute('data-resize-popup', '');
- };
- let resizePopup = (function () {
- let timer;
- let fix = function () {
- timer = undefined;
- let doc = document;
- // Manually adjust the position of the main matrix according to the
- // height of the toolbar/matrix header.
- let paddingTop =
- (doc.querySelector('.paneHead').clientHeight + 2) + 'px';
- let paneContent = doc.querySelector('.paneContent');
- if (paddingTop !== paneContent.style.paddingTop) {
- paneContent.style.setProperty('padding-top', paddingTop);
- vAPI.localStorage.setItem('paneContentPaddingTop', paddingTop);
- }
- document
- .body
- .classList
- .toggle('hConstrained',
- window.innerWidth < document.body.clientWidth);
- popupWasResized();
- };
- return function () {
- if (timer !== undefined) {
- clearTimeout(timer);
- }
- timer = vAPI.setTimeout(fix, 97);
- };
- })();
- // Must be consistent with definitions in matrix.js
- let Dark = 0x80;
- let Red = 1;
- let Green = 2;
- let DarkRed = Dark | Red;
- let DarkGreen = Dark | Green;
- let matrixSnapshot = {};
- let groupsSnapshot = [];
- let allHostnamesSnapshot = 'do not leave this initial string empty';
- let matrixCellHotspots = null;
- let matrixHeaderPrettyNames = {
- 'all': '',
- 'cookie': '',
- 'css': '',
- 'image': '',
- 'media': '',
- 'script': '',
- 'xhr': '',
- 'frame': '',
- 'other': ''
- };
- let firstPartyLabel = '';
- let blacklistedHostnamesLabel = '';
- let expandosIdGenerator = 1;
- let nodeToExpandosMap = (function () {
- if (typeof window.Map === 'function') {
- return new window.Map();
- }
- })();
- let expandosFromNode = function (node) {
- if (node instanceof HTMLElement === false
- && typeof node.nodeAt === 'function') {
- node = node.nodeAt(0);
- }
- if (nodeToExpandosMap) {
- let expandosId = node.getAttribute('data-expandos');
- if (!expandosId) {
- expandosId = '' + (expandosIdGenerator++);
- node.setAttribute('data-expandos', expandosId);
- }
- let expandos = nodeToExpandosMap.get(expandosId);
- if (expandos === undefined) {
- expandos = Object.create(null);
- nodeToExpandosMap.set(expandosId, expandos);
- }
- return expandos;
- }
- return node;
- };
- function getUserSetting(setting) {
- return matrixSnapshot.userSettings[setting];
- }
- function setUserSetting(setting, value) {
- matrixSnapshot.userSettings[setting] = value;
- vAPI.messaging.send('popup.js', {
- what: 'userSettings',
- name: setting,
- value: value
- });
- }
- function getUISetting(setting) {
- let r = vAPI.localStorage.getItem(setting);
- if (typeof r !== 'string') {
- return undefined;
- }
- return JSON.parse(r);
- }
- function setUISetting(setting, value) {
- vAPI.localStorage.setItem(setting, JSON.stringify(value));
- }
- function updateMatrixSnapshot() {
- matrixSnapshotPoller.pollNow();
- }
- // For display purpose, create four distinct groups of rows:
- // 0th: literal "1st-party" row
- // 1st: page domain's related
- // 2nd: whitelisted
- // 3rd: graylisted
- // 4th: blacklisted
- function getGroupStats() {
- // Try to not reshuffle groups around while popup is opened if
- // no new hostname added.
- let latestDomainListSnapshot =
- Object.keys(matrixSnapshot.rows).sort().join();
- if (latestDomainListSnapshot === allHostnamesSnapshot) {
- return groupsSnapshot;
- }
- allHostnamesSnapshot = latestDomainListSnapshot;
- // First, group according to whether at least one node in the domain
- // hierarchy is white or blacklisted
- let pageDomain = matrixSnapshot.domain;
- let rows = matrixSnapshot.rows;
- let anyTypeOffset = matrixSnapshot.headerIndices.get('*');
- let hostname, domain;
- let row, color, count;
- let domainToGroupMap = {};
- // These have hard-coded position which cannot be overriden
- domainToGroupMap['1st-party'] = 0;
- domainToGroupMap[pageDomain] = 1;
- // 1st pass: domain wins if it has an explicit rule or a count
- for (hostname in rows) {
- if (rows.hasOwnProperty(hostname) === false) {
- continue;
- }
- if (hostname === '*' || hostname === '1st-party') {
- continue;
- }
- domain = rows[hostname].domain;
- if (domain === pageDomain || hostname !== domain) {
- continue;
- }
- row = rows[domain];
- color = row.temporary[anyTypeOffset];
- if (color === DarkGreen) {
- domainToGroupMap[domain] = 2;
- continue;
- }
- if (color === DarkRed) {
- domainToGroupMap[domain] = 4;
- continue;
- }
- count = row.counts[anyTypeOffset];
- if (count !== 0) {
- domainToGroupMap[domain] = 3;
- continue;
- }
- }
- // 2nd pass: green wins
- for (hostname in rows) {
- if (rows.hasOwnProperty(hostname) === false) {
- continue;
- }
- row = rows[hostname];
- domain = row.domain;
- if (domainToGroupMap.hasOwnProperty(domain)) {
- continue;
- }
- color = row.temporary[anyTypeOffset];
- if (color === DarkGreen) {
- domainToGroupMap[domain] = 2;
- }
- }
- // 3rd pass: gray with count wins
- for (hostname in rows) {
- if (rows.hasOwnProperty(hostname) === false) {
- continue;
- }
- row = rows[hostname];
- domain = row.domain;
- if (domainToGroupMap.hasOwnProperty(domain)) {
- continue;
- }
- color = row.temporary[anyTypeOffset];
- count = row.counts[anyTypeOffset];
- if ( color !== DarkRed && count !== 0 ) {
- domainToGroupMap[domain] = 3;
- }
- }
- // 4th pass: red wins whatever is left
- for (hostname in rows) {
- if (rows.hasOwnProperty(hostname) === false) {
- continue;
- }
- row = rows[hostname];
- domain = row.domain;
- if (domainToGroupMap.hasOwnProperty(domain)) {
- continue;
- }
- color = row.temporary[anyTypeOffset];
- if (color === DarkRed) {
- domainToGroupMap[domain] = 4;
- }
- }
- // 5th pass: gray wins whatever is left
- for (hostname in rows) {
- if (rows.hasOwnProperty(hostname) === false) {
- continue;
- }
- domain = rows[hostname].domain;
- if (domainToGroupMap.hasOwnProperty(domain)) {
- continue;
- }
- domainToGroupMap[domain] = 3;
- }
- // Last pass: put each domain in a group
- let groups = [
- {}, {}, {}, {}, {}
- ];
- for (hostname in rows) {
- if (rows.hasOwnProperty(hostname) === false) {
- continue;
- }
- if ( hostname === '*' ) {
- continue;
- }
- domain = rows[hostname].domain;
- let groupIndex = domainToGroupMap[domain];
- let group = groups[groupIndex];
- if (group.hasOwnProperty(domain) === false) {
- group[domain] = {};
- }
- group[domain][hostname] = true;
- }
- groupsSnapshot = groups;
- return groups;
- }
- // helpers
- function getTemporaryColor(hostname, type) {
- return matrixSnapshot
- .rows[hostname]
- .temporary[matrixSnapshot.headerIndices.get(type)];
- }
- function getPermanentColor(hostname, type) {
- return matrixSnapshot
- .rows[hostname]
- .permanent[matrixSnapshot.headerIndices.get(type)];
- }
- function addCellClass(cell, hostname, type) {
- let cl = cell.classList;
- cl.add('matCell');
- cl.add('t' + getTemporaryColor(hostname, type).toString(16));
- cl.add('p' + getPermanentColor(hostname, type).toString(16));
- }
- // This is required for when we update the matrix while it is open:
- // the user might have collapsed/expanded one or more domains, and we don't
- // want to lose all his hardwork.
- function getCollapseState(domain) {
- let states = getUISetting('popupCollapseSpecificDomains');
- if (typeof states === 'object' && states[domain] !== undefined) {
- return states[domain];
- }
- return matrixSnapshot.collapseAllDomains === true;
- }
- function toggleCollapseState(elem) {
- if (elem.ancestors('#matHead.collapsible').length > 0) {
- toggleMainCollapseState(elem);
- } else {
- toggleSpecificCollapseState(elem);
- }
- popupWasResized();
- }
- function toggleMainCollapseState(uelem) {
- let matHead =
- uelem.ancestors('#matHead.collapsible').toggleClass('collapsed');
- let collapsed =
- matrixSnapshot.collapseAllDomains = matHead.hasClass('collapsed');
- uDom('#matList .matSection.collapsible')
- .toggleClass('collapsed', collapsed);
- setUserSetting('popupCollapseAllDomains', collapsed);
- let specificCollapseStates =
- getUISetting('popupCollapseSpecificDomains') || {};
- let domains = Object.keys(specificCollapseStates);
- for (let i=domains.length-1; i>=0; --i) {
- let domain = domains[i];
- if (specificCollapseStates[domain] === collapsed) {
- delete specificCollapseStates[domain];
- }
- }
- setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
- }
- function toggleSpecificCollapseState(uelem) {
- // Remember collapse state forever, but only if it is different
- // from main collapse switch.
- let section =
- uelem.ancestors('.matSection.collapsible').toggleClass('collapsed');
- let domain = expandosFromNode(section).domain;
- let collapsed = section.hasClass('collapsed');
- let mainCollapseState = matrixSnapshot.collapseAllDomains === true;
- let specificCollapseStates =
- getUISetting('popupCollapseSpecificDomains') || {};
- if (collapsed !== mainCollapseState) {
- specificCollapseStates[domain] = collapsed;
- setUISetting('popupCollapseSpecificDomains',
- specificCollapseStates);
- } else if (specificCollapseStates[domain] !== undefined) {
- delete specificCollapseStates[domain];
- setUISetting('popupCollapseSpecificDomains',
- specificCollapseStates);
- }
- }
- // Update count value of matrix cells(s)
- function updateMatrixCounts() {
- let matCells = uDom('.matrix .matRow.rw > .matCell');
- let matRow, matCell, count, counts;
- let headerIndices = matrixSnapshot.headerIndices;
- let rows = matrixSnapshot.rows;
- let expandos;
- for (let i=matCells.length-1; i>=0; --i) {
- matCell = matCells.nodeAt(i);
- expandos = expandosFromNode(matCell);
- if (expandos.hostname === '*' || expandos.reqType === '*') {
- continue;
- }
- matRow = matCell.parentNode;
- counts = matRow.classList.contains('meta') ? 'totals' : 'counts';
- count = rows[expandos.hostname][counts][headerIndices
- .get(expandos.reqType)];
- if (count === expandos.count) {
- continue;
- }
- expandos.count = count;
- matCell.textContent = cellTextFromCount(count);
- }
- }
- function cellTextFromCount(count) {
- if (count === 0) {
- return '\u00A0';
- }
- if (count < 100) {
- return count;
- }
- return '99+';
- }
- // Update color of matrix cells(s)
- // Color changes when rules change
- function updateMatrixColors() {
- let cells = uDom('.matrix .matRow.rw > .matCell').removeClass();
- let cell, expandos;
- for (let i=cells.length-1; i>=0; --i) {
- cell = cells.nodeAt(i);
- expandos = expandosFromNode(cell);
- addCellClass(cell, expandos.hostname, expandos.reqType);
- }
- popupWasResized();
- }
- // Update behavior of matrix:
- // - Whether a section is collapsible or not. It is collapsible if:
- // - It has at least one subdomain AND
- // - There is no explicit rule anywhere in the subdomain cells AND
- // - It is not part of group 3 (blacklisted hostnames)
- function updateMatrixBehavior() {
- matrixList = matrixList || uDom('#matList');
- let sections = matrixList.descendants('.matSection');
- let section, subdomainRows, subdomainRow;
- for (let i=sections.length-1; i>=0; --i) {
- section = sections.at(i);
- subdomainRows = section.descendants('.l2:not(.g4)');
- for (let j=subdomainRows.length-1; j>=0; --j) {
- subdomainRow = subdomainRows.at(j);
- subdomainRow.toggleClass('collapsible',
- subdomainRow
- .descendants('.t81,.t82')
- .length === 0);
- }
- section.toggleClass('collapsible',
- subdomainRows.filter('.collapsible').length > 0);
- }
- }
- // handle user interaction with filters
- function getCellAction(hostname, type, leaning) {
- let temporaryColor = getTemporaryColor(hostname, type);
- let hue = temporaryColor & 0x03;
- // Special case: root toggle only between two states
- if (type === '*' && hostname === '*') {
- return hue === Green ?
- 'blacklistMatrixCell' :
- 'whitelistMatrixCell';
- }
- // When explicitly blocked/allowed, can only graylist
- let saturation = temporaryColor & 0x80;
- if (saturation === Dark) {
- return 'graylistMatrixCell';
- }
- return leaning === 'whitelisting' ?
- 'whitelistMatrixCell' :
- 'blacklistMatrixCell';
- }
- function handleFilter(button, leaning) {
- // our parent cell knows who we are
- let cell = button.ancestors('div.matCell');
- let expandos = expandosFromNode(cell);
- let type = expandos.reqType;
- let desHostname = expandos.hostname;
- // https://github.com/gorhill/uMatrix/issues/24
- // No hostname can happen -- like with blacklist meta row
- if (desHostname === '') {
- return;
- }
- let request = {
- what: getCellAction(desHostname, type, leaning),
- srcHostname: matrixSnapshot.scope,
- desHostname: desHostname,
- type: type
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
- }
- function handleWhitelistFilter(button) {
- handleFilter(button, 'whitelisting');
- }
- function handleBlacklistFilter(button) {
- handleFilter(button, 'blacklisting');
- }
- let matrixRowPool = [];
- let matrixSectionPool = [];
- let matrixGroupPool = [];
- let matrixRowTemplate = null;
- let matrixList = null;
- let startMatrixUpdate = function () {
- matrixList = matrixList || uDom('#matList');
- matrixList.detach();
- let rows = matrixList.descendants('.matRow');
- rows.detach();
- matrixRowPool = matrixRowPool.concat(rows.toArray());
- let sections = matrixList.descendants('.matSection');
- sections.detach();
- matrixSectionPool = matrixSectionPool.concat(sections.toArray());
- let groups = matrixList.descendants('.matGroup');
- groups.detach();
- matrixGroupPool = matrixGroupPool.concat(groups.toArray());
- };
- let endMatrixUpdate = function () {
- // https://github.com/gorhill/httpswitchboard/issues/246 If
- // the matrix has no rows, we need to insert a dummy one,
- // invisible, to ensure the extension pop-up is properly
- // sized. This is needed because the header pane's `position`
- // property is `fixed`, which means it doesn't affect layout
- // size, hence the matrix header row will be truncated.
- if (matrixSnapshot.rowCount <= 1) {
- matrixList.append(createMatrixRow().css('visibility', 'hidden'));
- }
- updateMatrixBehavior();
- matrixList.css('display', '');
- matrixList.appendTo('.paneContent');
- };
- let createMatrixGroup = function () {
- let group = matrixGroupPool.pop();
- if (group) {
- return uDom(group).removeClass().addClass('matGroup');
- }
- return uDom(document.createElement('div')).addClass('matGroup');
- };
- let createMatrixSection = function () {
- let section = matrixSectionPool.pop();
- if (section) {
- return uDom(section).removeClass().addClass('matSection');
- }
- return uDom(document.createElement('div')).addClass('matSection');
- };
- let createMatrixRow = function () {
- let row = matrixRowPool.pop();
- if (row) {
- row.style.visibility = '';
- row = uDom(row);
- row.descendants('.matCell').removeClass().addClass('matCell');
- row.removeClass().addClass('matRow');
- return row;
- }
- if (matrixRowTemplate === null) {
- matrixRowTemplate = uDom('#templates .matRow');
- }
- return matrixRowTemplate.clone();
- };
- function renderMatrixHeaderRow() {
- let matHead = uDom('#matHead.collapsible');
- matHead.toggleClass('collapsed',
- matrixSnapshot.collapseAllDomains === true);
- let cells = matHead.descendants('.matCell')
- let cell = cells.nodeAt(0);
- let expandos = expandosFromNode(cell);
- expandos.reqType = '*';
- expandos.hostname = '*';
- addCellClass(cell, '*', '*');
- cell = cells.nodeAt(1);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'cookie';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'cookie');
- cell = cells.nodeAt(2);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'css';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'css');
- cell = cells.nodeAt(3);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'image';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'image');
- cell = cells.nodeAt(4);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'media';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'media');
- cell = cells.nodeAt(5);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'script';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'script');
- cell = cells.nodeAt(6);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'xhr';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'xhr');
- cell = cells.nodeAt(7);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'frame';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'frame');
- cell = cells.nodeAt(8);
- expandos = expandosFromNode(cell);
- expandos.reqType = 'other';
- expandos.hostname = '*';
- addCellClass(cell, '*', 'other');
- uDom('#matHead .matRow').css('display', '');
- }
- function renderMatrixCellDomain(cell, domain) {
- let expandos = expandosFromNode(cell);
- expandos.hostname = domain;
- expandos.reqType = '*';
- addCellClass(cell.nodeAt(0), domain, '*');
- let contents = cell.contents();
- contents.nodeAt(0).textContent = domain === '1st-party' ?
- firstPartyLabel :
- Punycode.toUnicode(domain);
- contents.nodeAt(1).textContent = ' ';
- }
- function renderMatrixCellSubdomain(cell, domain, subomain) {
- let expandos = expandosFromNode(cell);
- expandos.hostname = subomain;
- expandos.reqType = '*';
- addCellClass(cell.nodeAt(0), subomain, '*');
- let contents = cell.contents();
- contents.nodeAt(0).textContent =
- Punycode.toUnicode(subomain.slice(0,
- subomain.lastIndexOf(domain)-1))
- + '.';
- contents.nodeAt(1).textContent = Punycode.toUnicode(domain);
- }
- function renderMatrixMetaCellDomain(cell, domain) {
- let expandos = expandosFromNode(cell);
- expandos.hostname = domain;
- expandos.reqType = '*';
- addCellClass(cell.nodeAt(0), domain, '*');
- let contents = cell.contents();
- contents.nodeAt(0).textContent = '\u2217.' + Punycode.toUnicode(domain);
- contents.nodeAt(1).textContent = ' ';
- }
- function renderMatrixCellType(cell, hostname, type, count) {
- let node = cell.nodeAt(0);
- let expandos = expandosFromNode(node);
- expandos.hostname = hostname;
- expandos.reqType = type;
- expandos.count = count;
- addCellClass(node, hostname, type);
- node.textContent = cellTextFromCount(count);
- }
- function renderMatrixCellTypes(cells, hostname, countName) {
- let counts = matrixSnapshot.rows[hostname][countName];
- let headerIndices = matrixSnapshot.headerIndices;
- renderMatrixCellType(cells.at(1), hostname, 'cookie',
- counts[headerIndices.get('cookie')]);
- renderMatrixCellType(cells.at(2), hostname, 'css',
- counts[headerIndices.get('css')]);
- renderMatrixCellType(cells.at(3), hostname, 'image',
- counts[headerIndices.get('image')]);
- renderMatrixCellType(cells.at(4), hostname, 'media',
- counts[headerIndices.get('media')]);
- renderMatrixCellType(cells.at(5), hostname, 'script',
- counts[headerIndices.get('script')]);
- renderMatrixCellType(cells.at(6), hostname, 'xhr',
- counts[headerIndices.get('xhr')]);
- renderMatrixCellType(cells.at(7), hostname, 'frame',
- counts[headerIndices.get('frame')]);
- renderMatrixCellType(cells.at(8), hostname, 'other',
- counts[headerIndices.get('other')]);
- }
- function makeMatrixRowDomain(domain) {
- let matrixRow = createMatrixRow().addClass('rw');
- let cells = matrixRow.descendants('.matCell');
- renderMatrixCellDomain(cells.at(0), domain);
- renderMatrixCellTypes(cells, domain, 'counts');
- return matrixRow;
- }
- function makeMatrixRowSubdomain(domain, subdomain) {
- let matrixRow = createMatrixRow().addClass('rw');
- let cells = matrixRow.descendants('.matCell');
- renderMatrixCellSubdomain(cells.at(0), domain, subdomain);
- renderMatrixCellTypes(cells, subdomain, 'counts');
- return matrixRow;
- }
- function makeMatrixMetaRowDomain(domain) {
- let matrixRow = createMatrixRow().addClass('rw');
- let cells = matrixRow.descendants('.matCell');
- renderMatrixMetaCellDomain(cells.at(0), domain);
- renderMatrixCellTypes(cells, domain, 'totals');
- return matrixRow;
- }
- function renderMatrixMetaCellType(cell, count) {
- // https://github.com/gorhill/uMatrix/issues/24
- // Don't forget to reset cell properties
- let node = cell.nodeAt(0);
- let expandos = expandosFromNode(node);
- expandos.hostname = '';
- expandos.reqType = '';
- expandos.count = count;
- cell.addClass('t1');
- node.textContent = cellTextFromCount(count);
- }
- function makeMatrixMetaRow(totals) {
- let headerIndices = matrixSnapshot.headerIndices;
- let matrixRow = createMatrixRow().at(0).addClass('ro');
- let cells = matrixRow.descendants('.matCell');
- let contents = cells.at(0).addClass('t81').contents();
- let expandos = expandosFromNode(cells.nodeAt(0));
- expandos.hostname = '';
- expandos.reqType = '*';
- contents.nodeAt(0).textContent = ' ';
- contents.nodeAt(1).textContent =
- blacklistedHostnamesLabel
- .replace('{{count}}',
- totals[headerIndices.get('*')].toLocaleString());
- renderMatrixMetaCellType(cells.at(1),
- totals[headerIndices.get('cookie')]);
- renderMatrixMetaCellType(cells.at(2),
- totals[headerIndices.get('css')]);
- renderMatrixMetaCellType(cells.at(3),
- totals[headerIndices.get('image')]);
- renderMatrixMetaCellType(cells.at(4),
- totals[headerIndices.get('media')]);
- renderMatrixMetaCellType(cells.at(5),
- totals[headerIndices.get('script')]);
- renderMatrixMetaCellType(cells.at(6),
- totals[headerIndices.get('xhr')]);
- renderMatrixMetaCellType(cells.at(7),
- totals[headerIndices.get('frame')]);
- renderMatrixMetaCellType(cells.at(8),
- totals[headerIndices.get('other')]);
- return matrixRow;
- }
- function computeMatrixGroupMetaStats(group) {
- let headerIndices = matrixSnapshot.headerIndices;
- let anyTypeIndex = headerIndices.get('*');
- let totals = new Array(headerIndices.size);
- for (let i=headerIndices.size-1; i>=0; --i) {
- totals[i] = 0;
- }
- let rows = matrixSnapshot.rows;
- let row;
- for (let hostname in rows) {
- if (rows.hasOwnProperty(hostname) === false) {
- continue;
- }
- row = rows[hostname];
- if (group.hasOwnProperty(row.domain) === false) {
- continue;
- }
- if (row.counts[anyTypeIndex] === 0) {
- continue;
- }
- totals[0] += 1;
- for (let i=1; i<headerIndices.size; ++i) {
- totals[i] += row.counts[i];
- }
- }
- return totals;
- }
- // Compare hostname helper, to order hostname in a logical manner:
- // top-most < bottom-most, take into account whether IP address or
- // named hostname
- function hostnameCompare(a, b) {
- // Normalize: most significant parts first
- if (!a.match(/^\d+(\.\d+){1,3}$/)) {
- let aa = a.split('.');
- a = aa.slice(-2).concat(aa.slice(0,-2).reverse()).join('.');
- }
- if (!b.match(/^\d+(\.\d+){1,3}$/)) {
- let bb = b.split('.');
- b = bb.slice(-2).concat(bb.slice(0,-2).reverse()).join('.');
- }
- return a.localeCompare(b);
- }
- function makeMatrixGroup0SectionDomain() {
- return makeMatrixRowDomain('1st-party').addClass('g0 l1');
- }
- function makeMatrixGroup0Section() {
- let domainDiv = createMatrixSection();
- expandosFromNode(domainDiv).domain = '1st-party';
- makeMatrixGroup0SectionDomain().appendTo(domainDiv);
- return domainDiv;
- }
- function makeMatrixGroup0() {
- // Show literal "1st-party" row only if there is
- // at least one 1st-party hostname
- if (Object.keys(groupsSnapshot[1]).length === 0) {
- return;
- }
- let groupDiv = createMatrixGroup().addClass('g0');
- makeMatrixGroup0Section().appendTo(groupDiv);
- groupDiv.appendTo(matrixList);
- }
- function makeMatrixGroup1SectionDomain(domain) {
- return makeMatrixRowDomain(domain).addClass('g1 l1');
- }
- function makeMatrixGroup1SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain).addClass('g1 l2');
- }
- function makeMatrixGroup1SectionMetaDomain(domain) {
- return makeMatrixMetaRowDomain(domain).addClass('g1 l1 meta');
- }
- function makeMatrixGroup1Section(hostnames) {
- let domain = hostnames[0];
- let domainDiv =
- createMatrixSection().toggleClass('collapsed',
- getCollapseState(domain));
- expandosFromNode(domainDiv).domain = domain;
- if (hostnames.length > 1) {
- makeMatrixGroup1SectionMetaDomain(domain).appendTo(domainDiv);
- }
- makeMatrixGroup1SectionDomain(domain).appendTo(domainDiv);
- for (let i=1; i<hostnames.length; ++i) {
- makeMatrixGroup1SectionSubomain(domain, hostnames[i])
- .appendTo(domainDiv);
- }
- return domainDiv;
- }
- function makeMatrixGroup1(group) {
- let domains = Object.keys(group).sort(hostnameCompare);
- if (domains.length) {
- let groupDiv = createMatrixGroup().addClass('g1');
- makeMatrixGroup1Section(Object.keys(group[domains[0]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- for (let i=1; i<domains.length; ++i) {
- makeMatrixGroup1Section(Object.keys(group[domains[i]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- }
- groupDiv.appendTo(matrixList);
- }
- }
- function makeMatrixGroup2SectionDomain(domain) {
- return makeMatrixRowDomain(domain).addClass('g2 l1');
- }
- function makeMatrixGroup2SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain).addClass('g2 l2');
- }
- function makeMatrixGroup2SectionMetaDomain(domain) {
- return makeMatrixMetaRowDomain(domain).addClass('g2 l1 meta');
- }
- function makeMatrixGroup2Section(hostnames) {
- let domain = hostnames[0];
- let domainDiv =
- createMatrixSection().toggleClass('collapsed',
- getCollapseState(domain));
- expandosFromNode(domainDiv).domain = domain;
- if (hostnames.length > 1) {
- makeMatrixGroup2SectionMetaDomain(domain).appendTo(domainDiv);
- }
- makeMatrixGroup2SectionDomain(domain).appendTo(domainDiv);
- for (let i=1; i<hostnames.length; ++i) {
- makeMatrixGroup2SectionSubomain(domain, hostnames[i])
- .appendTo(domainDiv);
- }
- return domainDiv;
- }
- function makeMatrixGroup2(group) {
- let domains = Object.keys(group).sort(hostnameCompare);
- if (domains.length) {
- let groupDiv = createMatrixGroup().addClass('g2');
- makeMatrixGroup2Section(Object.keys(group[domains[0]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- for (let i=1; i<domains.length; ++i) {
- makeMatrixGroup2Section(Object.keys(group[domains[i]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- }
- groupDiv.appendTo(matrixList);
- }
- }
- function makeMatrixGroup3SectionDomain(domain) {
- return makeMatrixRowDomain(domain).addClass('g3 l1');
- }
- function makeMatrixGroup3SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain).addClass('g3 l2');
- }
- function makeMatrixGroup3SectionMetaDomain(domain) {
- return makeMatrixMetaRowDomain(domain).addClass('g3 l1 meta');
- }
- function makeMatrixGroup3Section(hostnames) {
- let domain = hostnames[0];
- let domainDiv = createMatrixSection().toggleClass('collapsed',
- getCollapseState(domain));
- expandosFromNode(domainDiv).domain = domain;
- if (hostnames.length > 1) {
- makeMatrixGroup3SectionMetaDomain(domain).appendTo(domainDiv);
- }
- makeMatrixGroup3SectionDomain(domain).appendTo(domainDiv);
- for (let i=1; i<hostnames.length; ++i) {
- makeMatrixGroup3SectionSubomain(domain, hostnames[i])
- .appendTo(domainDiv);
- }
- return domainDiv;
- }
- function makeMatrixGroup3(group) {
- let domains = Object.keys(group).sort(hostnameCompare);
- if (domains.length) {
- let groupDiv = createMatrixGroup().addClass('g3');
- makeMatrixGroup3Section(Object.keys(group[domains[0]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- for (let i=1; i<domains.length; ++i) {
- makeMatrixGroup3Section(Object.keys(group[domains[i]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- }
- groupDiv.appendTo(matrixList);
- }
- }
- function makeMatrixGroup4SectionDomain(domain) {
- return makeMatrixRowDomain(domain).addClass('g4 l1');
- }
- function makeMatrixGroup4SectionSubomain(domain, subdomain) {
- return makeMatrixRowSubdomain(domain, subdomain).addClass('g4 l2');
- }
- function makeMatrixGroup4Section(hostnames) {
- let domain = hostnames[0];
- let domainDiv = createMatrixSection();
- expandosFromNode(domainDiv).domain = domain;
- makeMatrixGroup4SectionDomain(domain).appendTo(domainDiv);
- for (let i=1; i<hostnames.length; ++i) {
- makeMatrixGroup4SectionSubomain(domain, hostnames[i])
- .appendTo(domainDiv);
- }
- return domainDiv;
- }
- function makeMatrixGroup4(group) {
- let domains = Object.keys(group).sort(hostnameCompare);
- if (domains.length === 0) {
- return;
- }
- let groupDiv = createMatrixGroup().addClass('g4');
- createMatrixSection()
- .addClass('g4Meta')
- .toggleClass('g4Collapsed',
- !!matrixSnapshot.collapseBlacklistedDomains)
- .appendTo(groupDiv);
- makeMatrixMetaRow(computeMatrixGroupMetaStats(group), 'g4')
- .appendTo(groupDiv);
- makeMatrixGroup4Section(Object.keys(group[domains[0]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- for (let i=1; i<domains.length; ++i) {
- makeMatrixGroup4Section(Object.keys(group[domains[i]])
- .sort(hostnameCompare))
- .appendTo(groupDiv);
- }
- groupDiv.appendTo(matrixList);
- }
- let makeMenu = function () {
- let groupStats = getGroupStats();
- if (Object.keys(groupStats).length === 0) {
- return;
- }
- // https://github.com/gorhill/httpswitchboard/issues/31
- if (matrixCellHotspots) {
- matrixCellHotspots.detach();
- }
- renderMatrixHeaderRow();
- startMatrixUpdate();
- makeMatrixGroup0(groupStats[0]);
- makeMatrixGroup1(groupStats[1]);
- makeMatrixGroup2(groupStats[2]);
- makeMatrixGroup3(groupStats[3]);
- makeMatrixGroup4(groupStats[4]);
- endMatrixUpdate();
- initScopeCell();
- updateMatrixButtons();
- resizePopup();
- };
- // Do all the stuff that needs to be done before building menu et al.
- function initMenuEnvironment() {
- document.body.style.setProperty('font-size',
- getUserSetting('displayTextSize'));
- document.body.classList.toggle('colorblind',
- getUserSetting('colorBlindFriendly'));
- uDom.nodeFromId('version').textContent =
- matrixSnapshot.appVersion || '';
- let prettyNames = matrixHeaderPrettyNames;
- let keys = Object.keys(prettyNames);
- for (let i=keys.length-1; i>=0; --i) {
- let key = keys[i];
- let cell = uDom('#matHead .matCell[data-req-type="'+ key +'"]');
- let text = vAPI.i18n(key + 'PrettyName');
- cell.text(text);
- prettyNames[key] = text;
- }
- firstPartyLabel = uDom('[data-i18n="matrix1stPartyLabel"]').text();
- blacklistedHostnamesLabel =
- uDom('[data-i18n="matrixBlacklistedHostnames"]').text();
- }
- // Create page scopes for the web page
- function selectGlobalScope() {
- if (matrixSnapshot.scope === '*') {
- return;
- }
- matrixSnapshot.scope = '*';
- document.body.classList.add('globalScope');
- matrixSnapshot.tMatrixModifiedTime = undefined;
- updateMatrixSnapshot();
- dropDownMenuHide();
- }
- function selectSpecificScope(ev) {
- let newScope = ev.target.getAttribute('data-scope');
- if (!newScope || matrixSnapshot.scope === newScope) {
- return;
- }
- document.body.classList.remove('globalScope');
- matrixSnapshot.scope = newScope;
- matrixSnapshot.tMatrixModifiedTime = undefined;
- updateMatrixSnapshot();
- dropDownMenuHide();
- }
- function initScopeCell() {
- // It's possible there is no page URL at this point: some pages cannot
- // be filtered by ηMatrix.
- if (matrixSnapshot.url === '') {
- return;
- }
- let specificScope = uDom.nodeFromId('specificScope');
- while (specificScope.firstChild !== null) {
- specificScope.removeChild(specificScope.firstChild);
- }
- // Fill in the scope menu entries
- let pos = matrixSnapshot.domain.indexOf('.');
- let tld, labels;
- if (pos === -1) {
- tld = '';
- labels = matrixSnapshot.hostname;
- } else {
- tld = matrixSnapshot.domain.slice(pos + 1);
- labels = matrixSnapshot.hostname.slice(0, -tld.length);
- }
- let beg = 0;
- let span, label;
- while (beg < labels.length) {
- pos = labels.indexOf('.', beg);
- if (pos === -1) {
- pos = labels.length;
- } else {
- pos += 1;
- }
- label = document.createElement('span');
- label.appendChild(document
- .createTextNode(Punycode
- .toUnicode(labels.slice(beg,
- pos))));
- span = document.createElement('span');
- span.setAttribute('data-scope', labels.slice(beg) + tld);
- span.appendChild(label);
- specificScope.appendChild(span);
- beg = pos;
- }
- if (tld !== '') {
- label = document.createElement('span');
- label.appendChild(document.createTextNode(Punycode.toUnicode(tld)));
- span = document.createElement('span');
- span.setAttribute('data-scope', tld);
- span.appendChild(label);
- specificScope.appendChild(span);
- }
- updateScopeCell();
- }
- function updateScopeCell() {
- let specificScope = uDom.nodeFromId('specificScope');
- let isGlobal = matrixSnapshot.scope === '*';
- document.body.classList.toggle('globalScope', isGlobal);
- specificScope.classList.toggle('on', !isGlobal);
- uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal);
- for (let node of specificScope.children) {
- node.classList.toggle('on',
- !isGlobal
- && matrixSnapshot
- .scope
- .endsWith(node.getAttribute('data-scope')));
- }
- }
- function updateMatrixSwitches() {
- let count = 0;
- let enabled;
- let switches = matrixSnapshot.tSwitches;
- for (let switchName in switches) {
- if (switches.hasOwnProperty(switchName) === false) {
- continue;
- }
- enabled = switches[switchName];
- if (enabled && switchName !== 'matrix-off') {
- count += 1;
- }
- uDom('#mtxSwitch_' + switchName).toggleClass('switchTrue', enabled);
- }
- uDom.nodeFromId('mtxSwitch_https-strict')
- .classList
- .toggle('relevant', matrixSnapshot.hasMixedContent);
- uDom.nodeFromId('mtxSwitch_no-workers')
- .classList
- .toggle('relevant', matrixSnapshot.hasWebWorkers);
- uDom.nodeFromId('mtxSwitch_referrer-spoof')
- .classList.toggle('relevant', matrixSnapshot.has3pReferrer);
- uDom.nodeFromId('mtxSwitch_noscript-spoof')
- .classList
- .toggle('relevant', matrixSnapshot.hasNoscriptTags);
- uDom.nodeFromSelector('#buttonMtxSwitches span.badge').textContent =
- count.toLocaleString();
- uDom.nodeFromSelector('#mtxSwitch_matrix-off span.badge').textContent =
- matrixSnapshot.blockedCount.toLocaleString();
- document.body.classList.toggle('powerOff', switches['matrix-off']);
- }
- function toggleMatrixSwitch(ev) {
- if (ev.target.localName === 'a') {
- return;
- }
- let elem = ev.currentTarget;
- let pos = elem.id.indexOf('_');
- if (pos === -1) {
- return;
- }
- let switchName = elem.id.slice(pos + 1);
- let request = {
- what: 'toggleMatrixSwitch',
- switchName: switchName,
- srcHostname: matrixSnapshot.scope
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
- }
- function updatePersistButton() {
- let diffCount = matrixSnapshot.diff.length;
- let button = uDom('#buttonPersist');
- button.contents()
- .filter(function () {
- return this.nodeType===3;
- })
- .first()
- .text(diffCount > 0 ? '\uf13e' : '\uf023');
- button.descendants('span.badge').text(diffCount > 0 ? diffCount : '');
- let disabled = diffCount === 0;
- button.toggleClass('disabled', disabled);
- uDom('#buttonRevertScope').toggleClass('disabled', disabled);
- }
- function persistMatrix() {
- let request = {
- what: 'applyDiffToPermanentMatrix',
- diff: matrixSnapshot.diff
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
- }
- // rhill 2014-03-12: revert completely ALL changes related to the
- // current page, including scopes.
- function revertMatrix() {
- let request = {
- what: 'applyDiffToTemporaryMatrix',
- diff: matrixSnapshot.diff
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
- }
- // Buttons which are affected by any changes in the matrix
- function updateMatrixButtons() {
- updateScopeCell();
- updateMatrixSwitches();
- updatePersistButton();
- }
- function revertAll() {
- let request = {
- what: 'revertTemporaryMatrix'
- };
- vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
- dropDownMenuHide();
- }
- function buttonReloadHandler(ev) {
- vAPI.messaging.send('popup.js', {
- what: 'forceReloadTab',
- tabId: matrixSnapshot.tabId,
- bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey
- });
- }
- function mouseenterMatrixCellHandler(ev) {
- matrixCellHotspots.appendTo(ev.target);
- }
- function mouseleaveMatrixCellHandler() {
- matrixCellHotspots.detach();
- }
- function gotoExtensionURL(ev) {
- let url = uDom(ev.currentTarget).attr('data-extension-url');
- if (url) {
- vAPI.messaging.send('popup.js', {
- what: 'gotoExtensionURL',
- url: url,
- shiftKey: ev.shiftKey
- });
- }
- dropDownMenuHide();
- vAPI.closePopup();
- }
- function dropDownMenuShow(ev) {
- let button = ev.target;
- let menuOverlay = document.getElementById(button.getAttribute('data-dropdown-menu'));
- let butnRect = button.getBoundingClientRect();
- let viewRect = document.body.getBoundingClientRect();
- let butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width);
- menuOverlay.classList.add('show');
- let menu = menuOverlay.querySelector('.dropdown-menu');
- let menuRect = menu.getBoundingClientRect();
- let menuLeft = butnNormalLeft * (viewRect.width - menuRect.width);
- menu.style.left = menuLeft.toFixed(0) + 'px';
- menu.style.top = butnRect.bottom + 'px';
- }
- function dropDownMenuHide() {
- uDom('.dropdown-menu-capture').removeClass('show');
- }
- let onMatrixSnapshotReady = function (response) {
- if (response === 'ENOTFOUND') {
- uDom.nodeFromId('noTabFound').textContent =
- vAPI.i18n('matrixNoTabFound');
- document.body.classList.add('noTabFound');
- return;
- }
- // Now that tabId and pageURL are set, we can build our menu
- initMenuEnvironment();
- makeMenu();
- // After popup menu is built, check whether there is a non-empty matrix
- if (matrixSnapshot.url === '') {
- uDom('#matHead').remove();
- uDom('#toolbarContainer').remove();
- // https://github.com/gorhill/httpswitchboard/issues/191
- uDom('#noNetTrafficPrompt').text(vAPI.i18n('matrixNoNetTrafficPrompt'));
- uDom('#noNetTrafficPrompt').css('display', '');
- }
- // Create a hash to find out whether the reload button needs to be
- // highlighted.
- // TODO:
- // ηMatrix: not sure what the purpose of highlighting is...
- // Maybe telling the user that the page needs refreshing? But
- // that's hardly useful (and by now people have gotten used to
- // the lack of such a feature.)
- // Not really going to do it, but let's leave the comment.
- };
- let matrixSnapshotPoller = (function () {
- let timer = null;
- let preprocessMatrixSnapshot = function (snapshot) {
- if (Array.isArray(snapshot.headerIndices)) {
- snapshot.headerIndices = new Map(snapshot.headerIndices);
- }
- return snapshot;
- };
- let processPollResult = function (response) {
- if (typeof response !== 'object') {
- return;
- }
- if (response.mtxContentModified === false
- && response.mtxCountModified === false
- && response.pMatrixModified === false
- && response.tMatrixModified === false) {
- return;
- }
- matrixSnapshot = preprocessMatrixSnapshot(response);
- if (response.mtxContentModified) {
- makeMenu();
- return;
- }
- if (response.mtxCountModified) {
- updateMatrixCounts();
- }
- if (response.pMatrixModified
- || response.tMatrixModified
- || response.scopeModified) {
- updateMatrixColors();
- updateMatrixBehavior();
- updateMatrixButtons();
- }
- };
- let onPolled = function (response) {
- processPollResult(response);
- pollAsync();
- };
- let pollNow = function () {
- unpollAsync();
- vAPI.messaging.send('popup.js', {
- what: 'matrixSnapshot',
- tabId: matrixSnapshot.tabId,
- scope: matrixSnapshot.scope,
- mtxContentModifiedTime: matrixSnapshot.mtxContentModifiedTime,
- mtxCountModifiedTime: matrixSnapshot.mtxCountModifiedTime,
- mtxDiffCount: matrixSnapshot.diff.length,
- pMatrixModifiedTime: matrixSnapshot.pMatrixModifiedTime,
- tMatrixModifiedTime: matrixSnapshot.tMatrixModifiedTime,
- }, onPolled);
- };
- let poll = function () {
- timer = null;
- pollNow();
- };
- let pollAsync = function () {
- if (timer !== null) {
- return;
- }
- if (document.defaultView === null) {
- return;
- }
- timer = vAPI.setTimeout(poll, 1414);
- };
- let unpollAsync = function () {
- if (timer !== null) {
- clearTimeout(timer);
- timer = null;
- }
- };
- (function () {
- let tabId = matrixSnapshot.tabId;
- // If no tab id yet, see if there is one specified in our URL
- if (tabId === undefined) {
- let matches = window
- .location
- .search
- .match(/(?:\?|&)tabId=([^&]+)/);
- if (matches !== null) {
- tabId = matches[1];
- // No need for logger button when embedded in logger
- uDom('[data-extension-url="logger-ui.html"]').remove();
- }
- }
- let snapshotFetched = function (response) {
- if (typeof response === 'object') {
- matrixSnapshot = preprocessMatrixSnapshot(response);
- }
- onMatrixSnapshotReady(response);
- pollAsync();
- };
- vAPI.messaging.send('popup.js', {
- what: 'matrixSnapshot',
- tabId: tabId
- }, snapshotFetched);
- })();
- return {
- pollNow: pollNow
- };
- })();
- // Below is UI stuff which is not key to make the menu, so this can
- // be done without having to wait for a tab to be bound to the menu.
- // We reuse for all cells the one and only cell hotspots.
- uDom('#whitelist').on('click', function () {
- handleWhitelistFilter(uDom(this));
- return false;
- });
- uDom('#blacklist').on('click', function () {
- handleBlacklistFilter(uDom(this));
- return false;
- });
- uDom('#domainOnly').on('click', function () {
- toggleCollapseState(uDom(this));
- return false;
- });
- matrixCellHotspots = uDom('#cellHotspots').detach();
- uDom('body')
- .on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
- .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
- uDom('#specificScope').on('click', selectSpecificScope);
- uDom('#globalScope').on('click', selectGlobalScope);
- uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch);
- uDom('#buttonPersist').on('click', persistMatrix);
- uDom('#buttonRevertScope').on('click', revertMatrix);
- uDom('#buttonRevertAll').on('click', revertAll);
- uDom('#buttonReload').on('click', buttonReloadHandler);
- uDom('.extensionURL').on('click', gotoExtensionURL);
- uDom('body').on('click', '[data-dropdown-menu]', dropDownMenuShow);
- uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide);
- uDom('#matList').on('click', '.g4Meta', function (ev) {
- matrixSnapshot.collapseBlacklistedDomains =
- ev.target.classList.toggle('g4Collapsed');
- setUserSetting('popupCollapseBlacklistedDomains',
- matrixSnapshot.collapseBlacklistedDomains);
- });
- })();
|