Permalink
Browse files

Add documentation from the code walkthrough

  • Loading branch information...
1 parent 6f79cb2 commit bad3f71b011a8f3c0d24c08603b98db3b7dae870 Monica Chew committed Feb 12, 2014
Showing with 87 additions and 8 deletions.
  1. +10 −0 data/aggregate.js
  2. +4 −0 data/content-script.js
  3. +1 −0 data/dialog.js
  4. +2 −1 data/events.js
  5. +6 −1 data/graph.js
  6. +1 −0 data/infobar.js
  7. +1 −0 data/tooltip.js
  8. +1 −0 data/ui.js
  9. +38 −4 lib/connection.js
  10. +6 −0 lib/main.js
  11. +1 −0 lib/tab/events.js
  12. +2 −1 lib/tab/utils.js
  13. +14 −1 lib/ui.js
View
@@ -5,6 +5,8 @@
(function(global){
"use strict";
+// An emitter that lists nodes and edges so we can build the data structure
+// used by all 3 visualizers.
var aggregate = new Emitter();
global.aggregate = aggregate;
global.filteredAggregate = {
@@ -14,6 +16,7 @@ global.filteredAggregate = {
aggregate.trackerCount = 0;
aggregate.siteCount = 0;
+// d3 has functionality to build graphs out of lists of nodes and edges.
aggregate.nodes = [];
aggregate.edges = [];
aggregate.recentSites = [];
@@ -123,12 +126,15 @@ function applyFilter(filter){
aggregate.on('filter', applyFilter);
+// Pass the list of connections to build the graph structure to pass to d3 for
+// visualizations.
function onLoad(connections){
// var startTime = Date.now();
// console.log('aggregate::onLoad with %s connections', connections.length);
connections.forEach(onConnection);
aggregate.initialized = true;
filteredAggregate = currentFilter();
+ // Tell the visualization that we're ready.
currentVisualization.emit('init');
updateStatsBar();
// console.log('aggregate::onLoad end, took %s ms', Date.now() - startTime);
@@ -152,6 +158,8 @@ aggregate.on('load', onLoad);
//const STATUS = 12;
//const CACHEABLE = 13;
+// Check that recent sites include the domain. This is another potential source
+// of false positives.
aggregate.isDomainVisited = function isDomainVisited(domain){
return aggregate.recentSites.length && (aggregate.recentSites.indexOf(domain) > -1);
}
@@ -257,11 +265,13 @@ function onBlocklistUpdate({ domain, flag }) {
}
aggregate.on('update-blocklist', onBlocklistUpdate);
+// Make sure all the blocklist is in local storage.
function onBlocklistUpdateAll(domains) {
(domains || []).forEach(onBlocklistUpdate);
}
aggregate.on('update-blocklist-all', onBlocklistUpdateAll);
+// Used only by the graph view.
function GraphEdge(source, target, connection){
var name = source.name + '->' + target.name;
if (aggregate.edgemap[name]){
@@ -1,3 +1,7 @@
+// This is the e10s/message passing content script that ties the workers to the
+// addon. It can see most of the addon, the window is either not visible or not
+// mutable so we use unsafeWindow below. This handles the post message
+// connections and does a little UI work on the side.
self.port.on('log', function log(args) {
if (unsafeWindow && unsafeWindow.console) {
unsafeWindow.console.log.call(unsafeWindow, args);
View
@@ -325,6 +325,7 @@ function showPromptToShareDialog(callback){
}
+// Helper function for testing so we can trigger any dialog.
function showDialog(name){
if (Object.keys(allDialogs).indexOf(name) > -1){
var tempPref = localStorage.dnsDialogs;
View
@@ -1,5 +1,6 @@
// Basic implementation of an event emitter for visualization plugins
-
+// This may be built-in to jetpack, but it's not available on the HTML side so
+// we need to keep this.
function Emitter(){
this._listeners = {};
}
View
@@ -1,11 +1,13 @@
-// Graph Visualization
+// Graph Visualization (one of 3 views: graph, clock, and list). This is way
+// too heavyweight for mobile right now.
// Visualization of tracking data interconnections
(function(visualizations){
"use strict";
+// The graph is an emitter with a default size.
var graph = new Emitter();
visualizations.graph = graph;
graph.name = "graph";
@@ -33,6 +35,8 @@ var highlight = {
blocked: true
};
+// Restart the simulation. This is only called when there's a new connection we
+// haven't seen before.
function onUpdate(){
// new nodes, reheat graph simulation
if (force){
@@ -51,6 +55,7 @@ function onUpdate(){
function onInit(){
// console.log('graph::onInit()');
// console.log('initializing graph from %s connections', filteredAggregate.nodes.length);
+ // Handles all of the panning and scaling.
vis = d3.select(vizcanvas);
// A D3 visualization has a two main components, data-shaping, and setting up the D3 callbacks
// This binds our data to the D3 visualization and sets up the callbacks
View
@@ -1,3 +1,4 @@
+// Used for managing the DOM for infobar part of the page
'use strict';
function initMap(){
View
@@ -1,3 +1,4 @@
+// Manage hover-over tooltips (works differently in graph, list, clock views)
(function(global){
var tooltipTimer;
View
@@ -1,3 +1,4 @@
+// Bunch of utilities related to UI elements.
const graphNodeRadius = {
"graph": 12,
"clock": 3
View
@@ -1,4 +1,4 @@
-// Connection object
+// Connection object. This module may try to do too many things.
//
// Convert an HTTP request (channel) to a loggable, visualizable connection object, if possible
@@ -21,10 +21,15 @@ var captureAndStoreConnections = true;
exports.Connection = Connection;
exports.addConnection = addConnection;
-// FIXME: Move persistence into a component
+// FIXME: Move persistence into a component. Persistence must be done on the
+// addon side, not on the page workers side.
/* BEGIN FLAG PERSISTENCE */
var restored = false;
var storage = ss.storage;
+
+// Load temporary connections, for when the user doesn't have the Lightbeam UI
+// open and so we can't access local storage. Instead we store them in simple
+// storage.
function restore(){
// only called when add-on is initialized, not when ui page is refreshed
Connection.emit('restored');
@@ -56,7 +61,7 @@ function addConnection(connection){
}
}
-// store connBatch to simple-storage
+// store connBatch to simple-storage until the user opens the UI.
function storeConnBatch(){
checkStorageQuota();
if (storage.tempConnections){
@@ -67,13 +72,18 @@ function storeConnBatch(){
connBatch.length = 0;
}
+// Delete oldest connections. When we hit the simple storage quota limit,
+// Firefox throws an exception that the user won't see. We tried switching to
+// indexdb (which is async) but abandoned it. localForage may be a viable
+// substitute.
function checkStorageQuota(){
while (ss.quotaUsage > 1){
var sliceStart = Math.floor(ss.storage.tempConnections.length*0.05);
ss.storage.tempConnections = ss.storage.tempConnections.slice(sliceStart);
}
}
+// Get eTLD + 1 (e.g., example.com from foo.example.com)
function getDomain(host) {
try {
return eTLDSvc.getBaseDomainFromHost(host);
@@ -90,6 +100,7 @@ function getDomain(host) {
Connection.getDomain = getDomain; // make it part of what we export
+// Get subdomain (e.g., foo from foo.example.com)
function getSubdomain(host){
var domain = getDomain(host);
return host.slice(0, host.length - domain.length);
@@ -100,17 +111,21 @@ function Connection() {};
Connection.restoredLogs = 0;
Connection.restoredObjects = 0;
+// subject comes from events.on("http-on-modify-request");
Connection.fromSubject = function(subject){
var conn = new Connection();
conn.restoreFromSubject(subject);
return conn;
}
+// Deserialize connections. Switching to indexdb will give a bump in storage
+// efficiency.
Connection.restore = function(obj){
var conn = new Connection();
if (Array.isArray(obj)){
return obj;
}else{
+ // Uses more memory, for legacy deserialization.
conn.restoreFromObject(obj);
return;
}
@@ -147,6 +162,10 @@ Connection.prototype.restoreFromObject = function(obj){
return self
};
+// Functions below may include legacy code from the first version of Collusion.
+
+// Find the page the URL was originally loaded from to determine whether this
+// happened in first or third-party context.
function getAjaxRequestHeader(channel){
var header = null;
try{
@@ -162,6 +181,10 @@ function getAjaxRequestHeader(channel){
return header;
}
+// Does this make sense from http-on-examine-response? The response will have
+// Set-Cookie headers, the request will have Cookie headers. We should
+// investigate nsIHttpChannel to make sure that the response channel has the
+// original request headers.
function hasCookie(channel){
try {
return !! channel.getRequestHeader('Cookie');
@@ -175,9 +198,14 @@ function getProtocol(uri){
}
+// See doc/Heuristic for determining first party-ness.md
Connection.prototype.restoreFromSubject = function(event){
// Check to see if this is in fact a third-party connection, if not, return
var channel = event.subject.QueryInterface(Ci.nsIHttpChannel);
+ // The referrer may not be set properly, especially in the case where HTTPS
+ // includes HTTP where the referrer is stripped due to mixed-content
+ // attacks. Also it doesn't work for RTC or WebSockets, but since we are
+ // only examining HTTP requests right now, that's all right.
var source = channel.referrer;
var target = channel.URI;
var targetDomain = getDomain(target.host);
@@ -198,11 +226,13 @@ Connection.prototype.restoreFromSubject = function(event){
// chances are the URL is about:blank, which has no host and throws an exception
// console.error('Error getting host from: ' + browserUri.spec);
}
+ // This is probably the largest source of false positives.
var sourceVisited = !isAjax && (browserDomain === targetDomain || browserSpec === 'about:blank');
// Connection.log('browserUri ' + browserUri.spec + (sourceVisited ? ' equals ' : ' does not equal') + ' target ' + ( target && target.spec));
if (sourceVisited){
source = target;
}else if (!source){
+ // This may introduce faulty data.
// console.error('No source for target ' + target.spec + ' (' + channel.referrer + ')');
source = target; // can't have a null source
}
@@ -259,6 +289,7 @@ Connection.prototype.restoreFromSubject = function(event){
this.timestamp = Date.now();
this.contentType = channel.contentType || 'text/plain';
this.cookie = cookie;
+ // The client went to this page in a first-party context.
this.sourceVisited = sourceVisited;
this.secure = isSecure;
this.sourcePathDepth = source.path.split('/').length - 1;
@@ -268,13 +299,14 @@ Connection.prototype.restoreFromSubject = function(event){
this.method = channel.requestMethod;
this.status = channel.responseStatus;
this.cacheable = !channel.isNoCacheResponse();
+ // We visualize private connections but never store them.
this.isPrivate = isPrivate;
this._sourceTab = tab; // Never logged, only for associating data with current tab
// console.error((sourceVisited ? 'site: ' : 'tracker: ') + sourceDomain + ' -> ' + targetDomain + ' (' + browserUri.spec + ')');
}
// Connection - level methods (not on instances)
-
+// This may be supported by the addon-sdk events.on now.
Connection.on = function(eventname, handler){
on(Connection, eventname, handler);
if (eventname === 'restored'){
@@ -376,5 +408,7 @@ Connection.on("clearStoredTempConnections", function(result){
/* END FLAG EVENTS */
/* BEGIN FLAG PERSISTENCE */
+// See https://github.com/mozilla/lightbeam/issues/472
+// We should be notifying on uninstall events and clean up HTML local storage.
if (!restored) restore();
/* END FLAG PERSISTENCE */
View
@@ -7,18 +7,23 @@ const { Connection, addConnection } = require('./connection');
const tabEvents = require('./tab/events');
const ui = require('./ui');
+// This is the heart of Lightbeam, we get all of our data from observing these
+// requests.
events.on("http-on-examine-response", function(subject) {
var connection = Connection.fromSubject(subject);
if (connection.valid){
addConnection(connection);
}
}, true);
+// Enables us to see log messages on console, not on web log.
Connection.on('log', function(message){
ui.emit('log', message);
});
+// This is not currently supported. The purpose is to visualize per-tab
+// connections.
function matchesCurrentTab(connection){
// this is a tabinfo object
var tabinfo = this;
@@ -29,6 +34,7 @@ function matchesCurrentTab(connection){
}
+// This lets us hook into page load events and communicate to page workers.
PageMod({
include: ui.mainPage,
contentScriptWhen: 'ready',
View
@@ -1,3 +1,4 @@
+// Simple onTab handler to figure out what tab a connection corresponds to.
'use strict';
const tabs = require('sdk/tabs');
View
@@ -46,7 +46,7 @@ function onTab(eventname, fn){
// Below code is based on adhacker, taken from http://forums.mozillazine.org/viewtopic.php?f=19&p=6335275
-
+// Erik Vold may have the most current information on this.
function getTabForChannel(aHttpChannel) {
var loadContext = getLoadContext(aHttpChannel);
if (!loadContext) {
@@ -65,6 +65,7 @@ function getTabForChannel(aHttpChannel) {
}
}
+// Special case in case we don't have a load context.
function getTabForChannel2(aChannel) {
var win = getWindowForChannel(aChannel);
if (!win) return null;
Oops, something went wrong.

0 comments on commit bad3f71

Please sign in to comment.