Skip to content

Commit 227c629

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents 8f1740a + 6c8cd5d commit 227c629

9 files changed

Lines changed: 329 additions & 1 deletion

File tree

chromium/background.js

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
// Records which tabId's are active in the HTTPS Switch Planner (see
2+
// devtools-panel.js).
3+
var switchPlannerEnabledFor = {};
4+
// Detailed information recorded when the HTTPS Switch Planner is active.
5+
// Structure is:
6+
// switchPlannerInfo[tabId]["rw"/"nrw"][resource_host][active_content][url];
7+
// rw / nrw stand for "rewritten" versus "not rewritten"
8+
var switchPlannerInfo = {};
19

210
var all_rules = new RuleSets();
311
var wr = chrome.webRequest;
@@ -141,6 +149,16 @@ function onBeforeRequest(details) {
141149
newuristr = finaluri.toString();
142150
}
143151

152+
// In Switch Planner Mode, record any non-rewriteable
153+
// HTTP URIs by parent hostname, along with the resource type.
154+
if (switchPlannerEnabledFor[details.tabId] && uri.protocol() !== "https") {
155+
writeToSwitchPlanner(details.type,
156+
details.tabId,
157+
canonical_host,
158+
details.url,
159+
newuristr);
160+
}
161+
144162
if (newuristr) {
145163
log(DBUG, "Redirecting from "+details.url+" to "+newuristr);
146164
return {redirectUrl: newuristr};
@@ -149,6 +167,157 @@ function onBeforeRequest(details) {
149167
}
150168
}
151169

170+
171+
// Map of which values for the `type' enum denote active vs passive content.
172+
// https://developer.chrome.com/extensions/webRequest.html#event-onBeforeRequest
173+
var activeTypes = { stylesheet: 1, script: 1, object: 1, other: 1};
174+
// We consider sub_frame to be passive even though it can contain JS or Flash.
175+
// This is because code running the sub_frame cannot access the main frame's
176+
// content, by same-origin policy. This is true even if the sub_frame is on the
177+
// same domain but different protocol - i.e. HTTP while the parent is HTTPS -
178+
// because same-origin policy includes the protocol. This also mimics Chrome's
179+
// UI treatment of insecure subframes.
180+
var passiveTypes = { main_frame: 1, sub_frame: 1, image: 1, xmlhttprequest: 1};
181+
182+
// Record a non-HTTPS URL loaded by a given hostname in the Switch Planner, for
183+
// use in determining which resources need to be ported to HTTPS.
184+
function writeToSwitchPlanner(type, tab_id, resource_host, resource_url, rewritten_url) {
185+
var rw = "rw";
186+
if (rewritten_url == null)
187+
rw = "nrw";
188+
189+
var active_content = 0;
190+
if (activeTypes[type]) {
191+
active_content = 1;
192+
} else if (passiveTypes[type]) {
193+
active_content = 0;
194+
} else {
195+
log(WARN, "Unknown type from onBeforeRequest details: `" + type + "', assuming active");
196+
active_content = 1;
197+
}
198+
199+
if (!switchPlannerInfo[tab_id]) {
200+
switchPlannerInfo[tab_id] = {};
201+
switchPlannerInfo[tab_id]["rw"] = {};
202+
switchPlannerInfo[tab_id]["nrw"] = {};
203+
}
204+
if (!switchPlannerInfo[tab_id][rw][resource_host])
205+
switchPlannerInfo[tab_id][rw][resource_host] = {};
206+
if (!switchPlannerInfo[tab_id][rw][resource_host][active_content])
207+
switchPlannerInfo[tab_id][rw][resource_host][active_content] = {};
208+
209+
switchPlannerInfo[tab_id][rw][resource_host][active_content][resource_url] = 1;
210+
}
211+
212+
// Return the number of properties in an object. For associative maps, this is
213+
// their size.
214+
function objSize(obj) {
215+
if (typeof obj == 'undefined') return 0;
216+
var size = 0, key;
217+
for (key in obj) {
218+
if (obj.hasOwnProperty(key)) size++;
219+
}
220+
return size;
221+
}
222+
223+
// Make an array of asset hosts by score so we can sort them,
224+
// presenting the most important ones first.
225+
function sortSwitchPlanner(tab_id, rewritten) {
226+
var asset_host_list = [];
227+
if (typeof switchPlannerInfo[tab_id] === 'undefined' ||
228+
typeof switchPlannerInfo[tab_id][rewritten] === 'undefined') {
229+
return [];
230+
}
231+
var tabInfo = switchPlannerInfo[tab_id][rewritten];
232+
for (var asset_host in tabInfo) {
233+
var ah = tabInfo[asset_host];
234+
var activeCount = objSize(ah[1]);
235+
var passiveCount = objSize(ah[0]);
236+
var score = activeCount * 100 + passiveCount;
237+
asset_host_list.push([score, activeCount, passiveCount, asset_host]);
238+
}
239+
asset_host_list.sort(function(a,b){return a[0]-b[0]});
240+
return asset_host_list;
241+
}
242+
243+
// Format the switch planner output for presentation to a user.
244+
function switchPlannerSmallHtmlSection(tab_id, rewritten) {
245+
var asset_host_list = sortSwitchPlanner(tab_id, rewritten);
246+
if (asset_host_list.length == 0) {
247+
return "<b>none</b>";
248+
}
249+
250+
var output = "";
251+
for (var i = asset_host_list.length - 1; i >= 0; i--) {
252+
var host = asset_host_list[i][3];
253+
var activeCount = asset_host_list[i][1];
254+
var passiveCount = asset_host_list[i][2];
255+
256+
output += "<b>" + host + "</b>: ";
257+
if (activeCount > 0) {
258+
output += activeCount + " active";
259+
if (passiveCount > 0)
260+
output += ", ";
261+
}
262+
if (passiveCount > 0) {
263+
output += passiveCount + " passive";
264+
}
265+
output += "<br/>";
266+
}
267+
return output;
268+
}
269+
270+
function switchPlannerRenderSections(tab_id, f) {
271+
return "Unrewritten HTTP resources loaded from this tab (enable HTTPS on " +
272+
"these domains and add them to HTTPS Everywhere):<br/>" +
273+
f(tab_id, "nrw") +
274+
"<br/>Resources rewritten successfully from this tab (update these " +
275+
"in your source code):<br/>" +
276+
f(tab_id, "rw");
277+
}
278+
279+
function switchPlannerSmallHtml(tab_id) {
280+
return switchPlannerRenderSections(tab_id, switchPlannerSmallHtmlSection);
281+
}
282+
283+
function linksFromKeys(map) {
284+
if (typeof map == 'undefined') return "";
285+
var output = "";
286+
for (var key in map) {
287+
if (map.hasOwnProperty(key)) {
288+
output += "<a href='" + key + "'>" + key + "</a><br/>";
289+
}
290+
}
291+
return output;
292+
}
293+
294+
function switchPlannerDetailsHtml(tab_id) {
295+
return switchPlannerRenderSections(tab_id, switchPlannerDetailsHtmlSection);
296+
}
297+
298+
function switchPlannerDetailsHtmlSection(tab_id, rewritten) {
299+
var asset_host_list = sortSwitchPlanner(tab_id, rewritten);
300+
var output = "";
301+
302+
for (var i = asset_host_list.length - 1; i >= 0; i--) {
303+
var host = asset_host_list[i][3];
304+
var activeCount = asset_host_list[i][1];
305+
var passiveCount = asset_host_list[i][2];
306+
307+
output += "<b>" + host + "</b>: ";
308+
if (activeCount > 0) {
309+
output += activeCount + " active<br/>";
310+
output += linksFromKeys(switchPlannerInfo[tab_id][rewritten][host][1]);
311+
}
312+
if (passiveCount > 0) {
313+
output += "<br/>" + passiveCount + " passive<br/>";
314+
output += linksFromKeys(switchPlannerInfo[tab_id][rewritten][host][0]);
315+
}
316+
output += "<br/>";
317+
}
318+
return output;
319+
}
320+
152321
function onCookieChanged(changeInfo) {
153322
if (!changeInfo.removed && !changeInfo.cookie.secure) {
154323
if (all_rules.shouldSecureCookie(changeInfo.cookie, false)) {
@@ -250,3 +419,36 @@ chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) {
250419
// Listen for cookies set/updated and secure them if applicable. This function is async/nonblocking,
251420
// so we also use onBeforeSendHeaders to prevent a small window where cookies could be stolen.
252421
chrome.cookies.onChanged.addListener(onCookieChanged);
422+
423+
function disableSwitchPlannerFor(tabId) {
424+
delete switchPlannerEnabledFor[tabId];
425+
// Clear stored URL info.
426+
delete switchPlannerInfo[tabId];
427+
}
428+
429+
function enableSwitchPlannerFor(tabId) {
430+
switchPlannerEnabledFor[tabId] = true;
431+
}
432+
433+
// Listen for connection from the DevTools panel so we can set up communication.
434+
chrome.runtime.onConnect.addListener(function (port) {
435+
if (port.name == "devtools-page") {
436+
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
437+
var tabId = message.tabId;
438+
439+
var disableOnCloseCallback = function(port) {
440+
log(DBUG, "Devtools window for tab " + tabId + " closed, clearing data.");
441+
disableSwitchPlannerFor(tabId);
442+
};
443+
444+
if (message.type === "enable") {
445+
enableSwitchPlannerFor(tabId);
446+
port.onDisconnect.addListener(disableOnCloseCallback);
447+
} else if (message.type === "disable") {
448+
disableSwitchPlannerFor(tabId);
449+
} else if (message.type === "getSmallHtml") {
450+
sendResponse({html: switchPlannerSmallHtml(tabId)});
451+
}
452+
});
453+
}
454+
});

chromium/devtools-panel.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<head>
2+
<title></title>
3+
<script src="devtools-panel.js"></script>
4+
</head>
5+
<body>
6+
<div id=SwitchPlanner>
7+
<input type=checkbox id=SwitchPlannerCheckbox>
8+
<label for=SwitchPlannerCheckbox>Enable HTTPS Switch Planner mode (reloads page).</label>
9+
<p>
10+
<div id=SwitchPlannerDescription>
11+
Switch Planner mode helps prepare for your site's switch to HTTPS by generating
12+
a report of external HTTP resources that might not yet be available on HTTPS.
13+
<p>
14+
After enabling, navigate around your site and try to exercise all
15+
functionality in order to get a comprehensive list of external resources.
16+
<p>
17+
For each group of resources listed as "Unrewritten," find out whether they
18+
are available on HTTPS. If so: add a rule to HTTPS Everywhere! If not: try
19+
to make them available over HTTPS or use a different resource or provider.
20+
Otherwise your site will generate
21+
<a id=MixedContentLink href="https://developer.mozilla.org/en-US/docs/Security/MixedContent#Mixed_passive.2Fdisplay_content">Mixed Content</a>
22+
(passive or active) errors when you turn on HTTPS.
23+
<p>
24+
For most accurate results, disable ad blockers before using. Closing this
25+
panel will deactivate Switch Planner mode and clear stored data.
26+
</div>
27+
<div id=SwitchPlannerResults style="display: none;">
28+
<div id=SwitchPlannerDetails></div>
29+
<a id=SwitchPlannerDetailsLink href="javascript:void(0);">details</a>
30+
</div>
31+
</div>
32+
</body>

chromium/devtools-panel.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
function e(id) {
2+
return document.getElementById(id);
3+
}
4+
5+
function sendMessage(type) {
6+
chrome.runtime.sendMessage({
7+
type: type,
8+
tabId: chrome.devtools.inspectedWindow.tabId,
9+
});
10+
}
11+
12+
// Turn on the Switch Planner recording mode, and hide the long description.
13+
function enableSwitchPlanner() {
14+
sendMessage("enable");
15+
e("SwitchPlannerDescription").style.display = "none";
16+
e("SwitchPlannerDetails").style.display = "block";
17+
// Hack: Fetch and display summary information from background page
18+
// once per second.
19+
setInterval(display, 1000);
20+
chrome.devtools.inspectedWindow.reload();
21+
}
22+
23+
// Disable the switch planner and reload, so any state is forgotten and
24+
// the long description is restored.
25+
function disableSwitchPlanner() {
26+
sendMessage("disable");
27+
document.location.reload();
28+
}
29+
30+
// Fetch summary HTML of the planner results from the background page for
31+
// display in the devtools panel.
32+
function display() {
33+
chrome.runtime.sendMessage({
34+
type: "getSmallHtml",
35+
tabId: chrome.devtools.inspectedWindow.tabId,
36+
}, function(response) {
37+
e("SwitchPlannerDetails").innerHTML = response.html;
38+
e("SwitchPlannerResults").style.display = "block";
39+
});
40+
}
41+
42+
window.onload = function() {
43+
// Open a connection to the background page. Right now this is only used
44+
// by the background page so it knows when the devtools pane has closed.
45+
// We don't receive messages from the background page currently, though that
46+
// may be a future improvement. Sending messages to the background page doesn't
47+
// require an existing connection.
48+
chrome.runtime.connect({ name: "devtools-page" });
49+
50+
var checkbox = e("SwitchPlannerCheckbox");
51+
checkbox.addEventListener("change", function() {
52+
if (checkbox.checked) {
53+
enableSwitchPlanner();
54+
} else {
55+
disableSwitchPlanner();
56+
}
57+
});
58+
59+
e("SwitchPlannerDetailsLink").addEventListener("click", function() {
60+
window.open("switch-planner.html?tab=" + chrome.devtools.inspectedWindow.tabId);
61+
});
62+
// Since this is rendered in a devtools console, we have to make clicks on the
63+
// link open a new window.
64+
e("MixedContentLink").addEventListener("click", function(e) {
65+
window.open(e.target.href);
66+
});
67+
};

chromium/devtools.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<head>
2+
<title></title>
3+
<script src="devtools.js"></script>
4+
</head>
5+
<body>
6+
</body>

chromium/devtools.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
chrome.devtools.panels.create("HTTPS Everywhere",
2+
"icon48.png",
3+
"devtools-panel.html",
4+
function(panel) {
5+
}
6+
);

chromium/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"default_popup": "popup.html",
2929
"default_title": "__MSG_about_ext_name__"
3030
},
31+
"devtools_page": "devtools.html",
3132
"permissions": [
3233
"webRequest",
3334
"webRequestBlocking",

chromium/rules.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ RuleSets.prototype = {
187187
// Have we cached this result? If so, return it!
188188
var cached_item = ruleCache.get(host);
189189
if (cached_item !== undefined) {
190-
log(DBUG, "Rulseset cache hit for " + host);
190+
log(DBUG, "Ruleset cache hit for " + host);
191191
return cached_item;
192192
}
193193
log(DBUG, "Ruleset cache miss for " + host);

chromium/switch-planner.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<head>
2+
<title></title>
3+
<script src="switch-planner.js"></script>
4+
</head>
5+
<body>
6+
<div id=content />
7+
</body>

chromium/switch-planner.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
window.onload = function() {
2+
var backgroundPage = chrome.extension.getBackgroundPage();
3+
var tab = document.location.search.match(/tab=([^&]*)/)[1];
4+
document.getElementById("content").innerHTML =
5+
backgroundPage.switchPlannerDetailsHtml(tab);
6+
};
7+

0 commit comments

Comments
 (0)