Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 75 additions & 55 deletions chrome/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,25 @@ var tab_listeners = {};
var tab_push = {}, tab_lasturl = {};
var selectedId = -1;

function refreshCount() {
txt = tab_listeners[selectedId] ? tab_listeners[selectedId].length : 0;
chrome.tabs.get(selectedId, function() {
if (!chrome.runtime.lastError) {
chrome.browserAction.setBadgeText({"text": ''+txt, tabId: selectedId});
if(txt > 0) {
chrome.browserAction.setBadgeBackgroundColor({ color: [255, 0, 0, 255]});
} else {
chrome.browserAction.setBadgeBackgroundColor({ color: [0, 0, 255, 0] });
}
}
});
async function refreshCount(tabId) {
const key = `tab_${tabId}`;
const data = await chrome.storage.session.get(key);
const listeners = data[key] ? data[key].listeners : [];
const txt = listeners ? listeners.length : 0;
chrome.action.setBadgeText({ "text": '' + txt, tabId: tabId });
if (txt > 0) {
chrome.action.setBadgeBackgroundColor({ color: [255, 0, 0, 255], tabId: tabId });
} else {
chrome.action.setBadgeBackgroundColor({ color: [0, 0, 255, 0], tabId: tabId });
}
}

function logListener(data) {
chrome.storage.sync.get({
log_url: ''
}, function(items) {
}, function (items) {
log_url = items.log_url;
if(!log_url.length) return;
if (!log_url.length) return;
data = JSON.stringify(data);
try {
fetch(log_url, {
Expand All @@ -31,67 +30,88 @@ function logListener(data) {
},
body: data
});
} catch(e) { }
} catch (e) { }
});
}

chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
console.log('message from cs', msg);
tabId = sender.tab.id;
if(msg.listener) {
if(msg.listener == 'function () { [native code] }') return;
chrome.runtime.onMessage.addListener(async function (msg, sender, sendResponse) {
const tabId = sender.tab.id;
const key = `tab_${tabId}`;
let tabData = (await chrome.storage.session.get(key))[key] || {};

if (msg.listener) {
if (msg.listener == 'function () { [native code] }') return;
msg.parent_url = sender.tab.url;
if(!tab_listeners[tabId]) tab_listeners[tabId] = [];
tab_listeners[tabId][tab_listeners[tabId].length] = msg;
if (!tabData.listeners) tabData.listeners = [];
tabData.listeners.push(msg);
logListener(msg);
}
if(msg.pushState) {
tab_push[tabId] = true;
if (msg.pushState) {
tabData.pushState = true;
}
if(msg.changePage) {
delete tab_lasturl[tabId];
if (msg.changePage) {
delete tabData.lastUrl;
}
if(msg.log) {
console.log(msg.log);
} else {
refreshCount();
await chrome.storage.session.set({ [key]: tabData });

if (!msg.log) {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (tab && tab.id === tabId) {
refreshCount(tabId);
}
}
});

chrome.tabs.onUpdated.addListener(function(tabId, props) {
console.log(props);
chrome.tabs.onUpdated.addListener(async function (tabId, props) {
const key = `tab_${tabId}`;
let tabData = (await chrome.storage.session.get(key))[key] || {};

if (props.status == "complete") {
if(tabId == selectedId) refreshCount();
} else if(props.status) {
if(tab_push[tabId]) {
//this was a pushState, ignore
delete tab_push[tabId];
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (tab && tab.id === tabId) {
refreshCount(tabId);
}
} else if (props.status) {
if (tabData.pushState) {
delete tabData.pushState;
} else {
//if(props.url && tab_lasturl[tabId] && props.url.split('#')[0] == tab_lasturl[tabId]) {
//same url as before, only a hash change, ignore
//} else
if(!tab_lasturl[tabId]) {
//wipe on other statuses, but only if lastpage is not set (aka, changePage did not run)
tab_listeners[tabId] = [];
if (!tabData.lastUrl) {
tabData.listeners = [];
}
}
}
if(props.status == "loading")
tab_lasturl[tabId] = true;
if (props.status == "loading") {
tabData.lastUrl = true;
}

await chrome.storage.session.set({ [key]: tabData });
});

chrome.tabs.onActivated.addListener(function(activeInfo) {
selectedId = activeInfo.tabId;
refreshCount();
chrome.tabs.onActivated.addListener(function (activeInfo) {
refreshCount(activeInfo.tabId);
});

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
selectedId = tabs[0].id;
refreshCount();
chrome.tabs.onRemoved.addListener(async (tabId) => {
const key = `tab_${tabId}`;
await chrome.storage.session.remove(key);
});

chrome.extension.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
port.postMessage({listeners:tab_listeners});
chrome.runtime.onConnect.addListener(function (port) {
port.onMessage.addListener(async function (msg) {
if (msg === "get-stuff") {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (tab) {
const key = `tab_${tab.id}`;
const data = await chrome.storage.session.get(key);
port.postMessage({ listeners: data[key] ? data[key].listeners : [] });
}
}
});
})
})

chrome.runtime.onInstalled.addListener(async () => {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (tabs.length > 0) {
refreshCount(tabs[0].id);
}
});
13 changes: 8 additions & 5 deletions chrome/content_script.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,16 @@ window.addEventListener('beforeunload', function(event) {
document.dispatchEvent(storeEvent);
});

(function() {
function injectScript() {
switch(document.contentType) {
case 'application/xml':
return;
}
var script = document.createElement("script");
script.setAttribute('type', 'text/javascript')
script.appendChild(document.createTextNode(injectedJS));
document.documentElement.appendChild(script);
})();
script.src = chrome.runtime.getURL('injected.js');
(document.head || document.documentElement).appendChild(script);
script.onload = function() {
script.remove();
};
}
injectScript();
202 changes: 202 additions & 0 deletions chrome/injected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
(function (pushstate, msgeventlistener, msgporteventlistener) {
var loaded = false;
var originalFunctionToString = Function.prototype.toString;
var m = function (detail) {
var storeEvent = new CustomEvent('postMessageTracker', { 'detail': detail });
document.dispatchEvent(storeEvent);
};
var h = function (p) {
var hops = "";
try {
if (!p) p = window;
if (p.top != p && p.top == window.top) {
var w = p;
while (top != w) {
var x = 0;
for (var i = 0; i < w.parent.frames.length; i++) {
if (w == w.parent.frames[i]) x = i;
};
hops = "frames[" + x + "]" + (hops.length ? '.' : '') + hops;
w = w.parent;
};
hops = "top" + (hops.length ? '.' + hops : '')
} else {
hops = p.top == window.top ? "top" : "diffwin";
}
} catch (e) {

}
return hops;
};
var jq = function (instance) {
if (!instance || !instance.message || !instance.message.length) return;
var j = 0; while (e = instance.message[j++]) {
listener = e.handler; if (!listener) return;
m({ window: window.top == window ? 'top' : window.name, hops: h(), domain: document.domain, stack: 'jQuery', listener: listener.toString() });
};
};
var l = function (listener, pattern_before, additional_offset) {
offset = 3 + (additional_offset || 0)
try { throw new Error(''); } catch (error) { stack = error.stack || ''; }
stack = stack.split('\n').map(function (line) { return line.trim(); });
fullstack = stack.slice();
if (pattern_before) {
nextitem = false;
stack = stack.filter(function (e) {
if (nextitem) { nextitem = false; return true; }
if (e.match(pattern_before))
nextitem = true;
return false;
});
stack = stack[0];
} else {
stack = stack[offset];
}
listener_str = listener.__postmessagetrackername__ || listener.toString();
m({ window: window.top == window ? 'top' : window.name, hops: h(), domain: document.domain, stack: stack, fullstack: fullstack, listener: listener_str });
};
var jqc = function (key) {
m({ log: ['Found key', key, typeof window[key], window[key] ? window[key].toString() : window[key]] });
if (typeof window[key] == 'function' && typeof window[key]._data == 'function') {
m({ log: ['found jq function', window[key].toString()] });
ev = window[key]._data(window, 'events');
jq(ev);
} else if (window[key] && (expando = window[key].expando)) {
m({ log: ['Use expando', expando] });
var i = 1; while (instance = window[expando + i++]) {
jq(instance.events);
}
} else if (window[key]) {
m({ log: ['Use events directly', window[key].toString()] });
jq(window[key].events);
}
};
var j = function () {
m({ log: 'Run jquery fetcher' });
var all = Object.getOwnPropertyNames(window);
var len = all.length;
for (var i = 0; i < len; i++) {
var key = all[i];
if (key.indexOf('jQuery') !== -1) {
jqc(key);
}
}
loaded = true;
};
History.prototype.pushState = function (state, title, url) {
m({ pushState: true });
return pushstate.apply(this, arguments);
};
var original_setter = window.__lookupSetter__('onmessage');
window.__defineSetter__('onmessage', function (listener) {
if (listener) {
l(listener.toString());
}
original_setter(listener);
});
var c = function (listener) {
var listener_str = originalFunctionToString.apply(listener)
if (listener_str.match(/\.deep.*apply.*captureException/s)) return 'raven';
else if (listener_str.match(/arguments.*(start|typeof).*err.*finally.*end/s) && listener["nr@original"] && typeof listener["nr@original"] == "function") return 'newrelic';
else if (listener_str.match(/rollbarContext.*rollbarWrappedError/s) && listener._isWrap &&
(typeof listener._wrapped == "function" || typeof listener._rollbar_wrapped == "function")) return 'rollbar';
else if (listener_str.match(/autoNotify.*(unhandledException|notifyException)/s) && typeof listener.bugsnag == "function") return 'bugsnag';
else if (listener_str.match(/call.*arguments.*typeof.*apply/s) && typeof listener.__sentry_original__ == "function") return 'sentry';
else if (listener_str.match(/function.*function.*\.apply.*arguments/s) && typeof listener.__trace__ == "function") return 'bugsnag2';
return false;
}

var onmsgport = function (e) {
var p = (e.ports.length ? '%cport' + e.ports.length + '%c ' : '');
var msg = '%cport%c→%c' + h(e.source) + '%c ' + p + (typeof e.data == 'string' ? e.data : 'j ' + JSON.stringify(e.data));
if (p.length) {
console.log(msg, "color: blue", '', "color: red", '', "color: blue", '');
} else {
console.log(msg, "color: blue", '', "color: red", '');
}
};
var onmsg = function (e) {
var p = (e.ports.length ? '%cport' + e.ports.length + '%c ' : '');
var msg = '%c' + h(e.source) + '%c→%c' + h() + '%c ' + p + (typeof e.data == 'string' ? e.data : 'j ' + JSON.stringify(e.data));
if (p.length) {
console.log(msg, "color: red", '', "color: green", '', "color: blue", '');
} else {
console.log(msg, "color: red", '', "color: green", '');
}
};
window.addEventListener('message', onmsg)
MessagePort.prototype.addEventListener = function (type, listener, useCapture) {
if (!this.__postmessagetrackername__) {
this.__postmessagetrackername__ = true;
this.addEventListener('message', onmsgport);
}
return msgporteventlistener.apply(this, arguments);
}

Window.prototype.addEventListener = function (type, listener, useCapture) {
if (type == 'message') {
var pattern_before = false, offset = 0;
if (listener.toString().indexOf('event.dispatch.apply') !== -1) {
m({ log: 'We got a jquery dispatcher' });
pattern_before = /init\.on|init\..*on\]/;
if (loaded) { setTimeout(j, 100); }
}
var unwrap = function (listener) {
found = c(listener);
if (found == 'raven') {
var fb = false, ff = false, v = null;
for (key in listener) {
var v = listener[key];
if (typeof v == "function") { ff++; f = v; }
if (typeof v == "boolean") fb++;
}
if (ff == 1 && fb == 1) {
m({ log: 'We got a raven wrapper' });
offset++;
listener = unwrap(f);
}
} else if (found == 'newrelic') {
m({ log: 'We got a newrelic wrapper' });
offset++;
listener = unwrap(listener["nr@original"]);
} else if (found == 'sentry') {
m({ log: 'We got a sentry wrapper' });
offset++;
listener = unwrap(listener["__sentry_original__"]);
} else if (found == 'rollbar') {
m({ log: 'We got a rollbar wrapper' });
offset += 2;
} else if (found == 'bugsnag') {
offset++;
var clr = null;
try { clr = arguments.callee.caller.caller.caller } catch (e) { }
if (clr && !c(clr)) {
m({ log: 'We got a bugsnag wrapper' });
listener.__postmessagetrackername__ = clr.toString();
} else if (clr) { offset++ }
} else if (found == 'bugsnag2') {
offset++;
var clr = null;
try { clr = arguments.callee.caller.caller.arguments[1]; } catch (e) { }
if (clr && !c(clr)) {
listener = unwrap(clr);
m({ log: 'We got a bugsnag2 wrapper' });
listener.__postmessagetrackername__ = clr.toString();
} else if (clr) { offset++; }
}
if (listener.name.indexOf('bound ') === 0) {
listener.__postmessagetrackername__ = listener.name;
}
return listener;
};

if (typeof listener == "function") {
listener = unwrap(listener);
l(listener, pattern_before, offset);
}
}
return msgeventlistener.apply(this, arguments);
};
window.addEventListener('load', j);
window.addEventListener('postMessageTrackerUpdate', j);
})(History.prototype.pushState, Window.prototype.addEventListener, MessagePort.prototype.addEventListener);
Loading